最高效率的对象深拷贝通用方法

本文介绍了一种高效实现对象深拷贝的方法,支持各类带有无参构造函数的类及一维数组的深拷贝,并能处理循环引用及双向链表结构。此方法通过缓存对象复制过程中的关键步骤来提高效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

[csharp] view plain copy
  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Linq;  
  4. using System.Reflection;  
  5. using System.Reflection.Emit;  
  6. using System.Text;  
  7.   
  8.   
  9. namespace Care.Common  
  10. {  
  11.     public static class ObjectCopy  
  12.     {  
  13.         struct Identity  
  14.         {  
  15.             int _hashcode;  
  16.             RuntimeTypeHandle _type;  
  17.   
  18.   
  19.             public Identity(int hashcode, RuntimeTypeHandle type)  
  20.             {  
  21.                 _hashcode = hashcode;  
  22.                 _type = type;  
  23.             }  
  24.         }  
  25.         //缓存对象复制的方法。  
  26.         static Dictionary<Type, Func<object, Dictionary<Identity, object>, object>> methods1 = new Dictionary<Type, Func<object, Dictionary<Identity, object>, object>>();  
  27.         static Dictionary<Type, Action<object, Dictionary<Identity, object>, object>> methods2 = new Dictionary<Type, Action<object, Dictionary<Identity, object>, object>>();  
  28.   
  29.   
  30.         static List<FieldInfo> GetSettableFields(Type t)  
  31.         {  
  32.             return t.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).ToList();  
  33.         }  
  34.   
  35.   
  36.         static Func<object, Dictionary<Identity, object>, object> CreateCloneMethod1(Type type, Dictionary<Identity, object> objects)  
  37.         {  
  38.             Type tmptype;  
  39.             var fields = GetSettableFields(type);  
  40.             var dm = new DynamicMethod(string.Format("Clone{0}", Guid.NewGuid()), typeof(object), new[] { typeof(object), typeof(Dictionary<Identity, object>) }, true);  
  41.             var il = dm.GetILGenerator();  
  42.             il.DeclareLocal(type);  
  43.             il.DeclareLocal(type);  
  44.             il.DeclareLocal(typeof(Identity));  
  45.             if (!type.IsArray)  
  46.             {  
  47.                 il.Emit(OpCodes.Newobj, type.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null));  
  48.                 il.Emit(OpCodes.Dup);  
  49.                 il.Emit(OpCodes.Stloc_1);  
  50.                 il.Emit(OpCodes.Ldloca_S, 2);  
  51.                 il.Emit(OpCodes.Ldarg_0);  
  52.                 il.Emit(OpCodes.Castclass, type);  
  53.                 il.Emit(OpCodes.Dup);  
  54.                 il.Emit(OpCodes.Stloc_0);  
  55.                 il.Emit(OpCodes.Callvirt, typeof(object).GetMethod("GetHashCode"));  
  56.                 il.Emit(OpCodes.Ldtoken, type);  
  57.                 il.Emit(OpCodes.Call, typeof(Identity).GetConstructor(BindingFlags.Instance | BindingFlags.Public, null, new[] { typeof(int), typeof(RuntimeTypeHandle) }, null));  
  58.                 il.Emit(OpCodes.Ldarg_1);  
  59.                 il.Emit(OpCodes.Ldloc_2);  
  60.                 il.Emit(OpCodes.Ldloc_1);  
  61.                 il.Emit(OpCodes.Callvirt, typeof(Dictionary<Identity, object>).GetMethod("Add"));  
  62.                 foreach (var field in fields)  
  63.                 {  
  64.                     if (!field.FieldType.IsValueType && field.FieldType != typeof(String))  
  65.                     {  
  66.                         //不符合条件的字段,直接忽略,避免报错。  
  67.                         if ((field.FieldType.IsArray && (field.FieldType.GetArrayRank() > 1 || (!(tmptype = field.FieldType.GetElementType()).IsValueType && tmptype != typeof(String) && tmptype.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null) == null))) ||  
  68.                             (!field.FieldType.IsArray && field.FieldType.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null) == null))  
  69.                             break;  
  70.                         il.Emit(OpCodes.Ldloc_1);  
  71.                         il.Emit(OpCodes.Ldloc_0);  
  72.                         il.Emit(OpCodes.Ldfld, field);  
  73.                         il.Emit(OpCodes.Ldarg_1);  
  74.                         il.EmitCall(OpCodes.Call, typeof(ObjectCopy).GetMethod("CopyImpl", BindingFlags.NonPublic | BindingFlags.Static).MakeGenericMethod(field.FieldType), null);  
  75.                         il.Emit(OpCodes.Stfld, field);  
  76.                     }  
  77.                     else  
  78.                     {  
  79.                         il.Emit(OpCodes.Ldloc_1);  
  80.                         il.Emit(OpCodes.Ldloc_0);  
  81.                         il.Emit(OpCodes.Ldfld, field);  
  82.                         il.Emit(OpCodes.Stfld, field);  
  83.                     }  
  84.                 }  
  85.                 for (type = type.BaseType; type != null && type != typeof(object); type = type.BaseType)  
  86.                 {  
  87.                     //只需要查找基类的私有成员,共有或受保护的在派生类中直接被复制过了。  
  88.                     fields = type.GetFields(BindingFlags.NonPublic | BindingFlags.Instance).ToList();  
  89.                     foreach (var field in fields)  
  90.                     {  
  91.                         if (!field.FieldType.IsValueType && field.FieldType != typeof(String))  
  92.                         {  
  93.                             //不符合条件的字段,直接忽略,避免报错。  
  94.                             if ((field.FieldType.IsArray && (field.FieldType.GetArrayRank() > 1 || (!(tmptype = field.FieldType.GetElementType()).IsValueType && tmptype != typeof(String) && tmptype.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null) == null))) ||  
  95.                                 (!field.FieldType.IsArray && field.FieldType.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null) == null))  
  96.                                 break;  
  97.                             il.Emit(OpCodes.Ldloc_1);  
  98.                             il.Emit(OpCodes.Ldloc_0);  
  99.                             il.Emit(OpCodes.Ldfld, field);  
  100.                             il.Emit(OpCodes.Ldarg_1);  
  101.                             il.EmitCall(OpCodes.Call, typeof(ObjectCopy).GetMethod("CopyImpl", BindingFlags.NonPublic | BindingFlags.Static).MakeGenericMethod(field.FieldType), null);  
  102.                             il.Emit(OpCodes.Stfld, field);  
  103.                         }  
  104.                         else  
  105.                         {  
  106.                             il.Emit(OpCodes.Ldloc_1);  
  107.                             il.Emit(OpCodes.Ldloc_0);  
  108.                             il.Emit(OpCodes.Ldfld, field);  
  109.                             il.Emit(OpCodes.Stfld, field);  
  110.                         }  
  111.                     }  
  112.                 }  
  113.             }  
  114.             else  
  115.             {  
  116.                 Type arraytype = type.GetElementType();  
  117.                 var i = il.DeclareLocal(typeof(int));  
  118.                 var lb1 = il.DefineLabel();  
  119.                 var lb2 = il.DefineLabel();  
  120.                 il.Emit(OpCodes.Ldarg_0);  
  121.                 il.Emit(OpCodes.Castclass, type);  
  122.                 il.Emit(OpCodes.Dup);  
  123.                 il.Emit(OpCodes.Stloc_0);  
  124.                 il.Emit(OpCodes.Ldlen);  
  125.                 il.Emit(OpCodes.Dup);  
  126.                 il.Emit(OpCodes.Ldc_I4_1);  
  127.                 il.Emit(OpCodes.Sub);  
  128.                 il.Emit(OpCodes.Stloc, i);  
  129.                 il.Emit(OpCodes.Newarr, arraytype);  
  130.                 il.Emit(OpCodes.Dup);  
  131.                 il.Emit(OpCodes.Stloc_1);  
  132.                 il.Emit(OpCodes.Ldloca_S, 2);  
  133.                 il.Emit(OpCodes.Ldloc_0);  
  134.                 il.Emit(OpCodes.Callvirt, typeof(object).GetMethod("GetHashCode"));  
  135.                 il.Emit(OpCodes.Ldtoken, type);  
  136.                 il.Emit(OpCodes.Call, typeof(Identity).GetConstructor(BindingFlags.Instance | BindingFlags.Public, null, new[] { typeof(int), typeof(RuntimeTypeHandle) }, null));  
  137.                 il.Emit(OpCodes.Ldarg_1);  
  138.                 il.Emit(OpCodes.Ldloc_2);  
  139.                 il.Emit(OpCodes.Ldloc_1);  
  140.                 il.Emit(OpCodes.Callvirt, typeof(Dictionary<Identity, object>).GetMethod("Add"));  
  141.                 il.Emit(OpCodes.Ldloc, i);  
  142.                 il.Emit(OpCodes.Br, lb1);  
  143.                 il.MarkLabel(lb2);  
  144.                 il.Emit(OpCodes.Dup);  
  145.                 il.Emit(OpCodes.Ldloc, i);  
  146.                 il.Emit(OpCodes.Ldloc_0);  
  147.                 il.Emit(OpCodes.Ldloc, i);  
  148.                 il.Emit(OpCodes.Ldelem, arraytype);  
  149.                 if (!arraytype.IsValueType && arraytype != typeof(String))  
  150.                 {  
  151.                     il.EmitCall(OpCodes.Call, typeof(ObjectCopy).GetMethod("CopyImpl", BindingFlags.NonPublic | BindingFlags.Static).MakeGenericMethod(arraytype), null);  
  152.                 }  
  153.                 il.Emit(OpCodes.Stelem, arraytype);  
  154.                 il.Emit(OpCodes.Ldloc, i);  
  155.                 il.Emit(OpCodes.Ldc_I4_1);  
  156.                 il.Emit(OpCodes.Sub);  
  157.                 il.Emit(OpCodes.Dup);  
  158.                 il.Emit(OpCodes.Stloc, i);  
  159.                 il.MarkLabel(lb1);  
  160.                 il.Emit(OpCodes.Ldc_I4_0);  
  161.                 il.Emit(OpCodes.Clt);  
  162.                 il.Emit(OpCodes.Brfalse, lb2);  
  163.             }  
  164.             il.Emit(OpCodes.Ret);  
  165.   
  166.   
  167.             return (Func<object, Dictionary<Identity, object>, object>)dm.CreateDelegate(typeof(Func<object, Dictionary<Identity, object>, object>));  
  168.         }  
  169.   
  170.         static Action<object, Dictionary<Identity, object>, object> CreateCloneMethod2(Type type, Dictionary<Identity, object> objects)  
  171.         {  
  172.             Type tmptype;  
  173.             var fields = GetSettableFields(type);  
  174.             var dm = new DynamicMethod(string.Format("Copy{0}", Guid.NewGuid()), null, new[] { typeof(object), typeof(Dictionary<Identity, object>), typeof(object) }, true);  
  175.             var il = dm.GetILGenerator();  
  176.             il.DeclareLocal(type);  
  177.             il.DeclareLocal(type);  
  178.             il.DeclareLocal(typeof(Identity));  
  179.             if (!type.IsArray)  
  180.             {  
  181.                 il.Emit(OpCodes.Ldarg_2);  
  182.                 il.Emit(OpCodes.Castclass, type);  
  183.                 il.Emit(OpCodes.Stloc_1);  
  184.                 il.Emit(OpCodes.Ldarg_0);  
  185.                 il.Emit(OpCodes.Castclass, type);  
  186.                 il.Emit(OpCodes.Stloc_0);  
  187.                 foreach (var field in fields)  
  188.                 {  
  189.                     if (!field.FieldType.IsValueType && field.FieldType != typeof(String))  
  190.                     {  
  191.                         //不符合条件的字段,直接忽略,避免报错。  
  192.                         if ((field.FieldType.IsArray && (field.FieldType.GetArrayRank() > 1 || (!(tmptype = field.FieldType.GetElementType()).IsValueType && tmptype != typeof(String) && tmptype.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null) == null))) ||  
  193.                             (!field.FieldType.IsArray && field.FieldType.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null) == null))  
  194.                             break;  
  195.                         il.Emit(OpCodes.Ldloc_1);  
  196.                         il.Emit(OpCodes.Ldloc_0);  
  197.                         il.Emit(OpCodes.Ldfld, field);  
  198.                         il.Emit(OpCodes.Ldarg_1);  
  199.                         il.EmitCall(OpCodes.Call, typeof(ObjectCopy).GetMethod("CopyImpl", BindingFlags.NonPublic | BindingFlags.Static).MakeGenericMethod(field.FieldType), null);  
  200.                         il.Emit(OpCodes.Stfld, field);  
  201.                     }  
  202.                     else  
  203.                     {  
  204.                         il.Emit(OpCodes.Ldloc_1);  
  205.                         il.Emit(OpCodes.Ldloc_0);  
  206.                         il.Emit(OpCodes.Ldfld, field);  
  207.                         il.Emit(OpCodes.Stfld, field);  
  208.                     }  
  209.                 }  
  210.                 for (type = type.BaseType; type != null && type != typeof(object); type = type.BaseType)  
  211.                 {  
  212.                     //只需要查找基类的私有成员,共有或受保护的在派生类中直接被复制过了。  
  213.                     fields = type.GetFields(BindingFlags.NonPublic | BindingFlags.Instance).ToList();  
  214.                     foreach (var field in fields)  
  215.                     {  
  216.                         if (!field.FieldType.IsValueType && field.FieldType != typeof(String))  
  217.                         {  
  218.                             //不符合条件的字段,直接忽略,避免报错。  
  219.                             if ((field.FieldType.IsArray && (field.FieldType.GetArrayRank() > 1 || (!(tmptype = field.FieldType.GetElementType()).IsValueType && tmptype != typeof(String) && tmptype.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null) == null))) ||  
  220.                                 (!field.FieldType.IsArray && field.FieldType.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null) == null))  
  221.                                 break;  
  222.                             il.Emit(OpCodes.Ldloc_1);  
  223.                             il.Emit(OpCodes.Ldloc_0);  
  224.                             il.Emit(OpCodes.Ldfld, field);  
  225.                             il.Emit(OpCodes.Ldarg_1);  
  226.                             il.EmitCall(OpCodes.Call, typeof(ObjectCopy).GetMethod("CopyImpl", BindingFlags.NonPublic | BindingFlags.Static).MakeGenericMethod(field.FieldType), null);  
  227.                             il.Emit(OpCodes.Stfld, field);  
  228.                         }  
  229.                         else  
  230.                         {  
  231.                             il.Emit(OpCodes.Ldloc_1);  
  232.                             il.Emit(OpCodes.Ldloc_0);  
  233.                             il.Emit(OpCodes.Ldfld, field);  
  234.                             il.Emit(OpCodes.Stfld, field);  
  235.                         }  
  236.                     }  
  237.                 }  
  238.             }  
  239.             else  
  240.             {  
  241.                 Type arraytype = type.GetElementType();  
  242.                 var i = il.DeclareLocal(typeof(int));  
  243.                 var lb1 = il.DefineLabel();  
  244.                 var lb2 = il.DefineLabel();  
  245.                 il.Emit(OpCodes.Ldarg_0);  
  246.                 il.Emit(OpCodes.Castclass, type);  
  247.                 il.Emit(OpCodes.Dup);  
  248.                 il.Emit(OpCodes.Stloc_0);  
  249.                 il.Emit(OpCodes.Ldlen);  
  250.                 il.Emit(OpCodes.Dup);  
  251.                 il.Emit(OpCodes.Ldc_I4_1);  
  252.                 il.Emit(OpCodes.Sub);  
  253.                 il.Emit(OpCodes.Stloc, i);  
  254.                 il.Emit(OpCodes.Ldarg_2);  
  255.                 il.Emit(OpCodes.Castclass, type);  
  256.                 il.Emit(OpCodes.Dup);  
  257.                 il.Emit(OpCodes.Stloc_1);  
  258.                 il.Emit(OpCodes.Ldloc, i);  
  259.                 il.Emit(OpCodes.Br, lb1);  
  260.                 il.MarkLabel(lb2);  
  261.                 il.Emit(OpCodes.Dup);  
  262.                 il.Emit(OpCodes.Ldloc, i);  
  263.                 il.Emit(OpCodes.Ldloc_0);  
  264.                 il.Emit(OpCodes.Ldloc, i);  
  265.                 il.Emit(OpCodes.Ldelem, arraytype);  
  266.                 if (!arraytype.IsValueType && arraytype != typeof(String))  
  267.                 {  
  268.                     il.EmitCall(OpCodes.Call, typeof(ObjectCopy).GetMethod("CopyImpl", BindingFlags.NonPublic | BindingFlags.Static).MakeGenericMethod(arraytype), null);  
  269.                 }  
  270.                 il.Emit(OpCodes.Stelem, arraytype);  
  271.                 il.Emit(OpCodes.Ldloc, i);  
  272.                 il.Emit(OpCodes.Ldc_I4_1);  
  273.                 il.Emit(OpCodes.Sub);  
  274.                 il.Emit(OpCodes.Dup);  
  275.                 il.Emit(OpCodes.Stloc, i);  
  276.                 il.MarkLabel(lb1);  
  277.                 il.Emit(OpCodes.Ldc_I4_0);  
  278.                 il.Emit(OpCodes.Clt);  
  279.                 il.Emit(OpCodes.Brfalse, lb2);  
  280.             }  
  281.             il.Emit(OpCodes.Ret);  
  282.   
  283.   
  284.             return (Action<object, Dictionary<Identity, object>, object>)dm.CreateDelegate(typeof(Action<object, Dictionary<Identity, object>, object>));  
  285.         }  
  286.   
  287.   
  288.         static T CopyImpl<T>(T source, Dictionary<Identity, object> objects) where T : class  
  289.         {  
  290.             //为空则直接返回null  
  291.             if (source == null)  
  292.                 return null;  
  293.   
  294.   
  295.             Type type = source.GetType();  
  296.             Identity id = new Identity(source.GetHashCode(), type.TypeHandle);  
  297.             object result;  
  298.             //如果发现曾经复制过,用之前的,从而停止递归复制。  
  299.             if (!objects.TryGetValue(id, out result))  
  300.             {  
  301.                 //最后查找对象的复制方法,如果不存在,创建新的。  
  302.                 Func<object, Dictionary<Identity, object>, object> method;  
  303.                 if (!methods1.TryGetValue(type, out method))  
  304.                 {  
  305.                     method = CreateCloneMethod1(type, objects);  
  306.                     methods1.Add(type, method);  
  307.                 }  
  308.                 result = method(source, objects);  
  309.             }  
  310.             return (T)result;  
  311.         }  
  312.   
  313.   
  314.   
  315.   
  316.         /// <summary>  
  317.         /// 创建对象深度复制的副本  
  318.         /// </summary>  
  319.         public static T ToObjectCopy<T>(this T source) where T : class  
  320.         {  
  321.             Type type = source.GetType();  
  322.             Dictionary<Identity, object> objects = new Dictionary<Identity, object>();//存放内嵌引用类型的复制链,避免构成一个环。  
  323.             Func<object, Dictionary<Identity, object>, object> method;  
  324.             if (!methods1.TryGetValue(type, out method))  
  325.             {  
  326.                 method = CreateCloneMethod1(type, objects);  
  327.                 methods1.Add(type, method);  
  328.             }  
  329.             return (T)method(source, objects);  
  330.         }  
  331.   
  332.   
  333.   
  334.   
  335.         /// <summary>  
  336.         /// 将source对象的所有属性复制到target对象中,深度复制  
  337.         /// </summary>  
  338.         public static void ObjectCopyTo<T>(this T source, T target) where T : class  
  339.         {  
  340.             if (target == null)  
  341.                 throw new Exception("将要复制的目标未初始化");  
  342.             Type type = source.GetType();  
  343.             if (type != target.GetType())  
  344.                 throw new Exception("要复制的对象类型不同,无法复制");  
  345.             Dictionary<Identity, object> objects = new Dictionary<Identity, object>();//存放内嵌引用类型的复制链,避免构成一个环。  
  346.             objects.Add(new Identity(source.GetHashCode(), type.TypeHandle), source);  
  347.             Action<object, Dictionary<Identity, object>, object> method;  
  348.             if (!methods2.TryGetValue(type, out method))  
  349.             {  
  350.                 method = CreateCloneMethod2(type, objects);  
  351.                 methods2.Add(type, method);  
  352.             }  
  353.             method(source, objects, target);  
  354.         }  
  355.     }  
  356. }  


   花了不少精力研究的最高效的通用方法,目前能支持任何带无参数的构造函数的类的深拷贝,一元数组的深拷贝,数组和类的循环嵌套深拷贝,双向链表结构 的类型对象深拷贝,进行对象拷贝过程中,未判断任何附加特性,因此可序列化标志对其无效,即使不可序列化的对象,也可以进行深拷贝。

  该方法是最基础的方法,如果需要高级应用,可以自定义特性(Attribute)进行忽略某些字段。

  该方法最多用途就是在数据库的实体对象的赋值上面,操作一个临时对象(复制出来),如果中途放弃改动,则直接丢弃对象,不影响原先的对象,否则可将对象复制回去。

转载于:https://my.oschina.net/zuochaoli/blog/95468

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值