怎样深度拷贝最快呢?
假设有这样的俩个对象
public class People
{
public int Age { get; set; }
public string Name { get; set; }
public string Sex { get; set; }
}
public class Man
{
public int ID { get;private set; }
public int Age { get; set; }
public string Name { get; set; }
public string Sex { get; set; }
}
如果我们将People的所有属性都赋值给Man。如果数量不多,我们一个个手写就好了,但是假设对象属性有成百上千个怎么办???一个个手写?那还不累死!
一般情况下有俩种办法
第一种,反射,逐个复制,但是我们都知道,反射是很慢的。性能并不好;
第二种,通过JsonConverter.序列化再反序列化,(⊙o⊙)…。性能比反射还差。
那有什么更好的办法吗???
第一种 通过字节缓冲。将成员信息缓冲到字典里面去,读取的时候再去里面查找。但是我们知道,字典是比较耗费时间的。
第二种 泛型缓冲,啥叫泛型缓冲?其实在C#里面 例如类 List<People> 在编译之后和 List<Man> 还有关系吗? 其实编译器是将其编译成为了俩单独的类。如果我们将我们的数据 存在类里面那么性能便是最高最快的。
废话说多了,贴代码。
public class DictionaryExpressionMapper
{
private static Dictionary<string, object> keyValuePairs = new Dictionary<string, object>();
public static TOut Copy<Tin, TOut>(Tin tin)
{
var key = $"funckey_{typeof(Tin).FullName}_{typeof(TOut).FullName}";
if (!keyValuePairs.ContainsKey(key))
{
var parameter = Expression.Parameter(typeof(Tin), "p");
List<MemberBinding> memberBindings = new List<MemberBinding>();
Copy<Tin>(typeof(TOut).GetProperties(), memberBindings, parameter);
var init = Expression.MemberInit(Expression.New(typeof(TOut)),memberBindings.ToArray());
var lambda = Expression.Lambda<Func<Tin, TOut>>(init, parameter);
keyValuePairs[key] = lambda.Compile();
}
return ((Func<Tin, TOut>)keyValuePairs[key]).Invoke(tin);
}
/// <summary>
/// 复制信息
/// </summary>
/// <typeparam name="Tin"></typeparam>
/// <param name="memberInfos">对象成员列表</param>
/// <param name="memberBindings">成员绑定表</param>
/// <param name="parameter">表达式参数</param>
private static void Copy<Tin>(IEnumerable<MemberInfo> memberInfos, List<MemberBinding> memberBindings, ParameterExpression parameter)
{
var propertys = typeof(Tin);
foreach (var item in memberInfos)
{
if (item is PropertyInfo property1 && property1.CanWrite == false)
{
continue;
}
var gg = propertys.GetProperty(item.Name);
if (gg == null)
{
continue;
}
var property = Expression.Property(parameter, gg);
var member = Expression.Bind(item, property);
memberBindings.Add(member);
}
}
}
上面的代码是干啥的?核心就是动态生成LINQ语句。逐个将属性读取出来,然后逐个编写LINQ 属性赋值语句,最后新建初始化TOUT。
我们知道 在字典里查找数据时其实是通过传入KEY的哈希值查找的,速度还是比较慢的。我们再次优化
public static class GenericExpressionMapper<Tin,TOut>
{
private static Func<Tin, TOut> func = null;
static GenericExpressionMapper()
{
var parameter = Expression.Parameter(typeof(Tin), "p");
List<MemberBinding> memberBindings = new List<MemberBinding>();
Copy(typeof(TOut).GetProperties(), memberBindings, parameter);
var init = Expression.MemberInit(Expression.New(typeof(TOut)), memberBindings.ToArray());
var lambda = Expression.Lambda<Func<Tin, TOut>>(init, parameter);
func = lambda.Compile();
}
/// <summary>
/// 深度复制
/// </summary>
/// <param name="tin"></param>
/// <returns></returns>
public static TOut Copy(Tin tin) => func.Invoke(tin);
/// <summary>
/// 复制信息
/// </summary>
/// <typeparam name="Tin"></typeparam>
/// <param name="memberInfos">对象成员列表</param>
/// <param name="memberBindings">成员绑定表</param>
/// <param name="parameter">表达式参数</param>
private static void Copy(IEnumerable<MemberInfo> memberInfos, List<MemberBinding> memberBindings, ParameterExpression parameter)
{
var propertys = typeof(Tin);
foreach (var item in memberInfos)
{
if (item is PropertyInfo property1 && property1.CanWrite == false)
{
continue;
}
var gg = propertys.GetProperty(item.Name);
if (gg == null)
{
continue;
}
var property = Expression.Property(parameter, gg);
var member = Expression.Bind(item, property);
memberBindings.Add(member);
}
}
}
升级后一对TIN和TOUT对应一个新的类。如此相当于我们直接操作类了,性能杠杠的。
调用就简单了。非常简单。如下
var peop = new People() {Age=123,Name="李四",Sex="女性" };
var res = GenericExpressionMapper<People, Man>.Copy(peop);
var re2 = GenericExpressionMapper<People, Man>.Copy(peop);
第一次调用泛型缓冲时,我们会调用其静态构造器,并生成其静态赋值委托。第二次调用时由于 静态构造器只调用一次,我们就直接调用其泛型委托了,啊哈,一下就赋值完成。
经过100万次赋值测试,泛型委托深拷贝方式 是 原生硬编码的俩倍。快不快?字典形式深拷贝是原生硬码方式的10倍。反射和序列化方式是原生硬编码的100多倍。
最后我们将上面的代码优化掉,一个扩展方法使用起来可就美滋滋了
/// <summary>
/// 克隆扩展
/// </summary>
public static class CloneExtension
{
private static Dictionary<string, object> keyValuePairs = new Dictionary<string, object>();
/// <summary>
/// 克隆复制当前对象
/// </summary>
/// <typeparam name="TIn"></typeparam>
/// <param name="data"></param>
/// <returns></returns>
public static TIn Clone<TIn>(this TIn data) => data.Clone<TIn, TIn>();
/// <summary>
/// 克隆服务对象
/// </summary>
/// <typeparam name="Tin"></typeparam>
/// <typeparam name="TOut"></typeparam>
/// <param name="tin"></param>
/// <returns></returns>
public static TOut Clone<Tin, TOut>(this Tin tin)
{
var key = $"funckey_{typeof(Tin).FullName}_{typeof(TOut).FullName}";
if (!keyValuePairs.ContainsKey(key))
{
var parameter = Expression.Parameter(typeof(Tin), "p");
List<MemberBinding> memberBindings = new List<MemberBinding>();
Copy<Tin>(typeof(TOut).GetProperties(), memberBindings, parameter);
var init = Expression.MemberInit(Expression.New(typeof(TOut)), memberBindings.ToArray());
var lambda = Expression.Lambda<Func<Tin, TOut>>(init, parameter);
keyValuePairs[key] = lambda.Compile();
}
return ((Func<Tin, TOut>)keyValuePairs[key]).Invoke(tin);
}
/// <summary>
/// 复制信息
/// </summary>
/// <typeparam name="Tin"></typeparam>
/// <param name="memberInfos">对象成员列表</param>
/// <param name="memberBindings">成员绑定表</param>
/// <param name="parameter">表达式参数</param>
private static void Copy<Tin>(IEnumerable<MemberInfo> memberInfos, List<MemberBinding> memberBindings, ParameterExpression parameter)
{
var propertys = typeof(Tin);
foreach (var item in memberInfos)
{
if (item is PropertyInfo property1 && property1.CanWrite == false)
{
continue;
}
var gg = propertys.GetProperty(item.Name);
if (gg == null)
{
continue;
}
var property = Expression.Property(parameter, gg);
var member = Expression.Bind(item, property);
memberBindings.Add(member);
}
}
}