【讨论】使用Expression动态克隆对象属性

Lee_Y_K 2015-12-28 03:22:39
在论坛里看到 wtoeb 使用反射来克隆对象属性(地址:http://bbs.csdn.net/topics/391883127?page=1),但是代码没有经过优化,实用性并不高,于是我做了一个使用表达式树进行优化的版本,这里仅实现了同类型克隆功能,只为提供一个思路,作为参考,欢迎大家拍砖




核心代码:


/// <summary>
/// Expression Clone
/// </summary>
public static class DynamicCloneHandler<T> where T : class
{
/// <summary>
/// 初始化
/// </summary>
static DynamicCloneHandler()
{
CloneMethod = CreateCloneMethod();
}
/// <summary>
/// 克隆函数
/// </summary>
private static Action<T, T> CloneMethod;
/// <summary>
/// 创建克隆方法
/// </summary>
/// <param name="type">类型</param>
/// <returns></returns>
private static Action<T, T> CreateCloneMethod()
{
Type type = typeof(T);
ParameterExpression tTarget = Expression.Parameter(type, "TTarget"),
tSource = Expression.Parameter(type, "TSource");
List<BinaryExpression> LsExp = new List<BinaryExpression>(16);
foreach (FieldInfo field in type.GetFields(BindingFlags.Public | BindingFlags.Instance)
.Union(type.GetFields(BindingFlags.NonPublic | BindingFlags.Instance)))
{
MemberExpression originalMember = Expression.Field(tSource, field);
MemberExpression newMember = Expression.Field(tTarget, field);
BinaryExpression setValue = Expression.Assign(originalMember, newMember);
LsExp.Add(setValue);
}
BlockExpression body = Expression.Block(typeof(void), LsExp);
return Expression.Lambda<Action<T, T>>(body, tTarget, tSource).Compile();
}
/// <summary>
/// 克隆 instance
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="instance">clone目标对象</param>
/// <param name="CreateMethod">创建函数</param>
/// <returns></returns>
public static T Clone(T instance, Func<T> CreateMethod)
{
T newIns = CreateMethod();
CloneMethod(newIns, instance);
return newIns;
}
public static T Clone(T instance, T newIns)
{
CloneMethod(newIns, instance);
return newIns;
}
}

因为使用 { get;set; } 这种方式定义“属性”,编译器会自动生成对应的 BackingField 字段,因此无需操作类型的“属性”



测试方法

创建两个类型,TestClass


public class TestClass1 : ICloneable
{
public int ID { get; set; }
public string Name { get; set; }
public string Test;
public int Code { get { return Name.GetHashCode(); } }
public object Clone()
{
return DynamicCloneHandler<TestClass1>.Clone(this, () => new TestClass1());
}
}


public class TestClass2 : ICloneable
{
public int ID { get; set; }
public string Name { get; set; }
public string Test;
public int Code { get { return Name.GetHashCode(); } }
public object Clone()
{
return new TestClass2 { ID = ID, Test = Test, Name = Name };
}
}

分别克隆1千万次


在这里两种克隆方式(直接赋值 和 反射+表达式树)的性能差距——会随着调用次数的增加而减少,间接说明了使用表达式树的方式可以获得接近直接赋值的性能(除了开始的时候反射创建克隆方法)

...全文
349 5 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
5 条回复
切换为时间正序
请发表友善的回复…
发表回复
Lee_Y_K 2015-12-30
  • 打赏
  • 举报
回复
引用 3 楼 kensouterry1 的回复:
不错不错,表达式树的方式可以提高性能,但复杂度杠杠的,顶一下先!
表达式树是个好东西,在动态兼顾性能的要求下是个不错的方案,相比Emit方式更“面向对象”。 另外上面的测试结果是在Debug下测试,所以看起来相差无几,如果是Release,1千万次的运行时间差在2-3倍左右,仍然能获得不错的性能。
本拉灯 2015-12-29
  • 打赏
  • 举报
回复
没用过。。。。
kensouterry1 2015-12-29
  • 打赏
  • 举报
回复
不错不错,表达式树的方式可以提高性能,但复杂度杠杠的,顶一下先!
  • 打赏
  • 举报
回复
前排占座
老衲是光头 2015-12-28
  • 打赏
  • 举报
回复
前排占座

111,094

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • AIGC Browser
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧