在面向對象編程的時代,實體類的操作已經越來越普遍了,從數據庫中獲取數據,離線存儲在實體類集合中,對集合的修改再反饋到數據庫中等。對於實體類的修改就意味着對數據庫的修改,為了更好的自動化編程,減少編碼量,同時也為了提高運行速度,這里發布個代碼類——對象的深度拷貝和對象屬性的淺拷貝, 利用了Emit操作,執行速度肯定是最快的。

public static class ObjectCopy { struct Identity { int _hashcode; RuntimeTypeHandle _type; public Identity(int hashcode, RuntimeTypeHandle type) { _hashcode = hashcode; _type = type; } } //緩存對象復制的方法。 static Dictionary<Type, Func<object, Dictionary<Identity, object>, object>> methods1 = new Dictionary<Type, Func<object, Dictionary<Identity, object>, object>>(); static Dictionary<Type, Action<object, Dictionary<Identity, object>, object>> methods2 = new Dictionary<Type, Action<object, Dictionary<Identity, object>, object>>(); static Dictionary<Type, Action<object, Dictionary<Identity, object>, object>> methods3 = new Dictionary<Type, Action<object, Dictionary<Identity, object>, object>>(); static List<FieldInfo> GetSettableFields(Type t) { return t.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).ToList(); } class PropInfo { public string Name { get; set; } public MethodInfo Getter { get; set; } public MethodInfo Setter { get; set; } public Type Type { get; set; } } static List<PropInfo> GetPublicProps(Type t) { return t .GetProperties() .Select(p => new PropInfo { Name = p.Name, Getter = p.DeclaringType == t ? p.GetGetMethod(true) : p.DeclaringType.GetProperty(p.Name).GetGetMethod(true), Setter = p.DeclaringType == t ? p.GetSetMethod(true) : p.DeclaringType.GetProperty(p.Name).GetSetMethod(true), Type = p.PropertyType }) .Where(info => info.Getter != null && info.Setter != null) .ToList(); } static Func<object, Dictionary<Identity, object>, object> CreateCloneMethod1(Type type, Dictionary<Identity, object> objects) { Type tmptype; var fields = GetSettableFields(type); var dm = new DynamicMethod(string.Format("Clone{0}", Guid.NewGuid()), typeof(object), new[] { typeof(object), typeof(Dictionary<Identity, object>) }, true); var il = dm.GetILGenerator(); il.DeclareLocal(type); il.DeclareLocal(type); il.DeclareLocal(typeof(Identity)); if (!type.IsArray) { il.Emit(OpCodes.Newobj, type.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null)); il.Emit(OpCodes.Dup); il.Emit(OpCodes.Stloc_1); il.Emit(OpCodes.Ldloca_S, 2); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Castclass, type); il.Emit(OpCodes.Dup); il.Emit(OpCodes.Stloc_0); il.Emit(OpCodes.Callvirt, typeof(object).GetMethod("GetHashCode")); il.Emit(OpCodes.Ldtoken, type); il.Emit(OpCodes.Call, typeof(Identity).GetConstructor(BindingFlags.Instance | BindingFlags.Public, null, new[] { typeof(int), typeof(RuntimeTypeHandle) }, null)); il.Emit(OpCodes.Ldarg_1); il.Emit(OpCodes.Ldloc_2); il.Emit(OpCodes.Ldloc_1); il.Emit(OpCodes.Callvirt, typeof(Dictionary<Identity, object>).GetMethod("Add")); foreach (var field in fields) { if (!field.FieldType.IsValueType && field.FieldType != typeof(String)) { //不符合條件的字段,直接忽略,避免報錯。 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))) || (!field.FieldType.IsArray && field.FieldType.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null) == null)) break; il.Emit(OpCodes.Ldloc_1); il.Emit(OpCodes.Ldloc_0); il.Emit(OpCodes.Ldfld, field); il.Emit(OpCodes.Ldarg_1); il.EmitCall(OpCodes.Call, typeof(ObjectCopy).GetMethod("CopyImpl1", BindingFlags.NonPublic | BindingFlags.Static).MakeGenericMethod(field.FieldType), null); il.Emit(OpCodes.Stfld, field); } else { il.Emit(OpCodes.Ldloc_1); il.Emit(OpCodes.Ldloc_0); il.Emit(OpCodes.Ldfld, field); il.Emit(OpCodes.Stfld, field); } } for (type = type.BaseType; type != null && type != typeof(object); type = type.BaseType) { //只需要查找基類的私有成員,共有或受保護的在派生類中直接被復制過了。 fields = type.GetFields(BindingFlags.NonPublic | BindingFlags.Instance).ToList(); foreach (var field in fields) { if (!field.FieldType.IsValueType && field.FieldType != typeof(String)) { //不符合條件的字段,直接忽略,避免報錯。 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))) || (!field.FieldType.IsArray && field.FieldType.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null) == null)) break; il.Emit(OpCodes.Ldloc_1); il.Emit(OpCodes.Ldloc_0); il.Emit(OpCodes.Ldfld, field); il.Emit(OpCodes.Ldarg_1); il.EmitCall(OpCodes.Call, typeof(ObjectCopy).GetMethod("CopyImpl1", BindingFlags.NonPublic | BindingFlags.Static).MakeGenericMethod(field.FieldType), null); il.Emit(OpCodes.Stfld, field); } else { il.Emit(OpCodes.Ldloc_1); il.Emit(OpCodes.Ldloc_0); il.Emit(OpCodes.Ldfld, field); il.Emit(OpCodes.Stfld, field); } } } } else { Type arraytype = type.GetElementType(); var i = il.DeclareLocal(typeof(int)); var lb1 = il.DefineLabel(); var lb2 = il.DefineLabel(); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Castclass, type); il.Emit(OpCodes.Dup); il.Emit(OpCodes.Stloc_0); il.Emit(OpCodes.Ldlen); il.Emit(OpCodes.Dup); il.Emit(OpCodes.Ldc_I4_1); il.Emit(OpCodes.Sub); il.Emit(OpCodes.Stloc, i); il.Emit(OpCodes.Newarr, arraytype); il.Emit(OpCodes.Dup); il.Emit(OpCodes.Stloc_1); il.Emit(OpCodes.Ldloca_S, 2); il.Emit(OpCodes.Ldloc_0); il.Emit(OpCodes.Callvirt, typeof(object).GetMethod("GetHashCode")); il.Emit(OpCodes.Ldtoken, type); il.Emit(OpCodes.Call, typeof(Identity).GetConstructor(BindingFlags.Instance | BindingFlags.Public, null, new[] { typeof(int), typeof(RuntimeTypeHandle) }, null)); il.Emit(OpCodes.Ldarg_1); il.Emit(OpCodes.Ldloc_2); il.Emit(OpCodes.Ldloc_1); il.Emit(OpCodes.Callvirt, typeof(Dictionary<Identity, object>).GetMethod("Add")); il.Emit(OpCodes.Ldloc, i); il.Emit(OpCodes.Br, lb1); il.MarkLabel(lb2); il.Emit(OpCodes.Dup); il.Emit(OpCodes.Ldloc, i); il.Emit(OpCodes.Ldloc_0); il.Emit(OpCodes.Ldloc, i); il.Emit(OpCodes.Ldelem, arraytype); if (!arraytype.IsValueType && arraytype != typeof(String)) { il.EmitCall(OpCodes.Call, typeof(ObjectCopy).GetMethod("CopyImpl1", BindingFlags.NonPublic | BindingFlags.Static).MakeGenericMethod(arraytype), null); } il.Emit(OpCodes.Stelem, arraytype); il.Emit(OpCodes.Ldloc, i); il.Emit(OpCodes.Ldc_I4_1); il.Emit(OpCodes.Sub); il.Emit(OpCodes.Dup); il.Emit(OpCodes.Stloc, i); il.MarkLabel(lb1); il.Emit(OpCodes.Ldc_I4_0); il.Emit(OpCodes.Clt); il.Emit(OpCodes.Brfalse, lb2); } il.Emit(OpCodes.Ret); return (Func<object, Dictionary<Identity, object>, object>)dm.CreateDelegate(typeof(Func<object, Dictionary<Identity, object>, object>)); } static Action<object, Dictionary<Identity, object>, object> CreateCloneMethod2(Type type, Dictionary<Identity, object> objects) { Type tmptype; var fields = GetSettableFields(type); var dm = new DynamicMethod(string.Format("Copy{0}", Guid.NewGuid()), null, new[] { typeof(object), typeof(Dictionary<Identity, object>), typeof(object) }, true); var il = dm.GetILGenerator(); il.DeclareLocal(type); il.DeclareLocal(type); il.DeclareLocal(typeof(Identity)); if (!type.IsArray) { il.Emit(OpCodes.Ldarg_2); il.Emit(OpCodes.Castclass, type); il.Emit(OpCodes.Stloc_1); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Castclass, type); il.Emit(OpCodes.Stloc_0); foreach (var field in fields) { if (!field.FieldType.IsValueType && field.FieldType != typeof(String)) { //不符合條件的字段,直接忽略,避免報錯。 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))) || (!field.FieldType.IsArray && field.FieldType.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null) == null)) break; il.Emit(OpCodes.Ldloc_1); il.Emit(OpCodes.Ldloc_0); il.Emit(OpCodes.Ldfld, field); il.Emit(OpCodes.Ldarg_1); il.EmitCall(OpCodes.Call, typeof(ObjectCopy).GetMethod("CopyImpl1", BindingFlags.NonPublic | BindingFlags.Static).MakeGenericMethod(field.FieldType), null); il.Emit(OpCodes.Stfld, field); } else { il.Emit(OpCodes.Ldloc_1); il.Emit(OpCodes.Ldloc_0); il.Emit(OpCodes.Ldfld, field); il.Emit(OpCodes.Stfld, field); } } for (type = type.BaseType; type != null && type != typeof(object); type = type.BaseType) { //只需要查找基類的私有成員,共有或受保護的在派生類中直接被復制過了。 fields = type.GetFields(BindingFlags.NonPublic | BindingFlags.Instance).ToList(); foreach (var field in fields) { if (!field.FieldType.IsValueType && field.FieldType != typeof(String)) { //不符合條件的字段,直接忽略,避免報錯。 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))) || (!field.FieldType.IsArray && field.FieldType.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null) == null)) break; il.Emit(OpCodes.Ldloc_1); il.Emit(OpCodes.Ldloc_0); il.Emit(OpCodes.Ldfld, field); il.Emit(OpCodes.Ldarg_1); il.EmitCall(OpCodes.Call, typeof(ObjectCopy).GetMethod("CopyImpl1", BindingFlags.NonPublic | BindingFlags.Static).MakeGenericMethod(field.FieldType), null); il.Emit(OpCodes.Stfld, field); } else { il.Emit(OpCodes.Ldloc_1); il.Emit(OpCodes.Ldloc_0); il.Emit(OpCodes.Ldfld, field); il.Emit(OpCodes.Stfld, field); } } } } else { Type arraytype = type.GetElementType(); var i = il.DeclareLocal(typeof(int)); var lb1 = il.DefineLabel(); var lb2 = il.DefineLabel(); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Castclass, type); il.Emit(OpCodes.Dup); il.Emit(OpCodes.Stloc_0); il.Emit(OpCodes.Ldlen); il.Emit(OpCodes.Dup); il.Emit(OpCodes.Ldc_I4_1); il.Emit(OpCodes.Sub); il.Emit(OpCodes.Stloc, i); il.Emit(OpCodes.Ldarg_2); il.Emit(OpCodes.Castclass, type); il.Emit(OpCodes.Dup); il.Emit(OpCodes.Stloc_1); il.Emit(OpCodes.Ldloc, i); il.Emit(OpCodes.Br, lb1); il.MarkLabel(lb2); il.Emit(OpCodes.Dup); il.Emit(OpCodes.Ldloc, i); il.Emit(OpCodes.Ldloc_0); il.Emit(OpCodes.Ldloc, i); il.Emit(OpCodes.Ldelem, arraytype); if (!arraytype.IsValueType && arraytype != typeof(String)) { il.EmitCall(OpCodes.Call, typeof(ObjectCopy).GetMethod("CopyImpl1", BindingFlags.NonPublic | BindingFlags.Static).MakeGenericMethod(arraytype), null); } il.Emit(OpCodes.Stelem, arraytype); il.Emit(OpCodes.Ldloc, i); il.Emit(OpCodes.Ldc_I4_1); il.Emit(OpCodes.Sub); il.Emit(OpCodes.Dup); il.Emit(OpCodes.Stloc, i); il.MarkLabel(lb1); il.Emit(OpCodes.Ldc_I4_0); il.Emit(OpCodes.Clt); il.Emit(OpCodes.Brfalse, lb2); } il.Emit(OpCodes.Ret); return (Action<object, Dictionary<Identity, object>, object>)dm.CreateDelegate(typeof(Action<object, Dictionary<Identity, object>, object>)); } /// <summary> /// 對屬性進行復制,不查找父類,用於當前實體對象的屬性拷貝。 /// </summary> /// <param name="type">實體類型</param> /// <param name="objects">復制鏈</param> /// <returns></returns> static Action<object, Dictionary<Identity, object>, object> CreateCloneMethod3(Type type, Dictionary<Identity, object> objects) { var props = GetPublicProps(type); var dm = new DynamicMethod(string.Format("Copy{0}", Guid.NewGuid()), null, new[] { typeof(object), typeof(Dictionary<Identity, object>), typeof(object) }, true); var il = dm.GetILGenerator(); il.DeclareLocal(type);//存放源對象 il.DeclareLocal(type);//存放目標對象 il.DeclareLocal(typeof(Identity));//存放標識 if (!type.IsArray) { il.Emit(OpCodes.Ldarg_2); il.Emit(OpCodes.Castclass, type); il.Emit(OpCodes.Stloc_1);//將參數中獲取的目標對象存放到局部變量1中 il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Castclass, type); il.Emit(OpCodes.Stloc_0);//將參數中獲取的源對象存放到局部變量0中 foreach (var prop in props) { if (!prop.Type.IsValueType && prop.Type != typeof(String)) { //不符合條件的屬性,直接忽略。 if (prop.Type.IsArray) break; il.Emit(OpCodes.Ldloc_0); il.Emit(OpCodes.Call, prop.Getter);//獲取局部變量0中暫存的源對象的屬性 il.Emit(OpCodes.Ldarg_1);//加載參數中的復制鏈對象 il.Emit(OpCodes.Ldloc_1); il.Emit(OpCodes.Call, prop.Getter);//獲取局部變量1中暫存的目標對象的屬性 il.EmitCall(OpCodes.Call, typeof(ObjectCopy).GetMethod("CopyImpl2", BindingFlags.NonPublic | BindingFlags.Static).MakeGenericMethod(prop.Type), null); } else { il.Emit(OpCodes.Ldloc_1); il.Emit(OpCodes.Ldloc_0); il.Emit(OpCodes.Call, prop.Getter); il.Emit(OpCodes.Call, prop.Setter); } } } il.Emit(OpCodes.Ret); return (Action<object, Dictionary<Identity, object>, object>)dm.CreateDelegate(typeof(Action<object, Dictionary<Identity, object>, object>)); } static T CopyImpl1<T>(T source, Dictionary<Identity, object> objects) where T : class { //為空則直接返回null if (source == null) return null; Type type = source.GetType(); Identity id = new Identity(source.GetHashCode(), type.TypeHandle); object result; //如果發現曾經復制過,用之前的,從而停止遞歸復制。 if (!objects.TryGetValue(id, out result)) { //最后查找對象的復制方法,如果不存在,創建新的。 Func<object, Dictionary<Identity, object>, object> method; if (!methods1.TryGetValue(type, out method)) { method = CreateCloneMethod1(type, objects); methods1.Add(type, method); } result = method(source, objects); } return (T)result; } static void CopyImpl2<T>(T source, T target, Dictionary<Identity, object> objects) where T : class { Type type = typeof(T); Identity id = new Identity(source.GetHashCode(), type.TypeHandle); object result; //如果發現曾經復制過,用之前的,從而停止遞歸復制。 if (!objects.TryGetValue(id, out result)) { objects.Add(new Identity(source.GetHashCode(), type.TypeHandle), source); //最后查找對象的復制方法,如果不存在,創建新的。 Action<object, Dictionary<Identity, object>, object> method; if (!methods3.TryGetValue(type, out method)) { method = CreateCloneMethod3(type, objects); methods3.Add(type, method); } method(source, objects, target); } } /// <summary> /// 創建對象深度復制的副本 /// </summary> public static T ToObjectCopy<T>(this T source) where T : class { Type type = source.GetType(); Dictionary<Identity, object> objects = new Dictionary<Identity, object>();//存放內嵌引用類型的復制鏈,避免構成一個環。 Func<object, Dictionary<Identity, object>, object> method; if (!methods1.TryGetValue(type, out method)) { method = CreateCloneMethod1(type, objects); methods1.Add(type, method); } return (T)method(source, objects); } /// <summary> /// 將source對象的所有屬性復制到target對象中,深度復制 /// </summary> public static void ObjectCopyTo<T>(this T source, T target) where T : class { if (target == null) throw new Exception("將要復制的目標未初始化"); Type type = source.GetType(); if (type != target.GetType()) throw new Exception("要復制的對象類型不同,無法復制"); Dictionary<Identity, object> objects = new Dictionary<Identity, object>();//存放內嵌引用類型的復制鏈,避免構成一個環。 objects.Add(new Identity(source.GetHashCode(), type.TypeHandle), source); Action<object, Dictionary<Identity, object>, object> method; if (!methods2.TryGetValue(type, out method)) { method = CreateCloneMethod2(type, objects); methods2.Add(type, method); } method(source, objects, target); } /// <summary> /// 將source對象的所有屬性復制到target對象中,僅對公有屬性復制,不查找父類,不創建引用屬性,但復制引用屬性內部的屬性。 /// </summary> public static void PropCopyTo<T>(this T source, T target) where T : class { if (target == null) throw new Exception("將要復制的目標未初始化"); Type type = typeof(T); Dictionary<Identity, object> objects = new Dictionary<Identity, object>();//存放內嵌引用類型的復制鏈,避免構成一個環。 objects.Add(new Identity(source.GetHashCode(), type.TypeHandle), source); Action<object, Dictionary<Identity, object>, object> method; if (!methods3.TryGetValue(type, out method)) { method = CreateCloneMethod3(type, objects); methods3.Add(type, method); } method(source, objects, target); } }
代碼中,ToObjectCopy是為了創建一個新的實體對象,ObjectCopyTo是為了將實體對象深度復制到另一個實體對象上面,PropCopyTo則是為了將對象屬性淺拷貝另一個對象中。
一、為什么要深度復制實體?
如果實體類中某個屬性是byte[]類型的,一般的賦值是傳遞地址,顯然不深度拷貝就不能完成類似引用類型屬性的真正拷貝。
二、如果實體類中某個實體的屬性是另一個實體類,是否會被復制?
會,因此設計實體類的時候,不要將引用關系直接設計到類的屬性中,雖然可以清晰的訪問父子關系,但是實體類在創建時難度就會增加,不利於自動化編程,實體就應該如實反映數據庫的表結構。
三、為什么要屬性的淺拷貝?
因為對象的深度拷貝是通過字段的直接賦值實現的,那樣效率最高,但是跳過了屬性訪問器,無法觸發屬性變化的事件,而對於實體類集合特別是ObservableCollection<T>需要通知UI刷新,需要觸發屬性變化的事件,一些數據庫自動更新也是通過屬性改變事件來觸發的。
四、什么時候要復制實體?
當我們需要一個對實體修改的臨時副本,不直接對數據源操作的時候,就需要用到它,這樣可以提供一個取消操作的可能——放棄被修改的實體對象即可。
曾在CSDN上發布過一個類似版本,這是改進版,都是本人原創。