如何实现A.ClassA到B.ClassB的深度拷贝(含集合等非基元类型)
问题描述
现有以下类定义:
namespace A; public class ClassA { public int A { get; set; } public string B { get; set; } public List<typeA> TypeA { get; set; } public TypeB TypeB { get; set; } } public class typeA { // 自定义属性 } public class TypeB { // 自定义属性 } namespace B; public class ClassB { public string BB { get; set; } public List<A.typeA> TypeA { get; set; } }
尝试使用以下代码将A.ClassA的数据拷贝到B.ClassB:
public class CopyClass { public static void CopyObject<T>(object sourceObject, ref T destObject) { if (sourceObject == null || destObject == null) return; Type sourceType = sourceObject.GetType(); Type targetType = destObject.GetType(); // 遍历源对象属性 foreach (PropertyInfo p in sourceType.GetProperties()) { PropertyInfo targetObj = targetType.GetProperty(p.Name); // 目标无对应属性则跳过 if (targetObj == null) continue; try { if (p.GetType() == targetObj.GetType()) targetObj.SetValue(destObject, p.GetValue(sourceObject, null), null); // 非基元类型此处抛出异常 } catch (Exception ex) { Console.WriteLine($"Exception copying : ex.Message = {ex.Message}, inner = {ex.InnerException?.Message}"); } } } }
问题:拷贝非基元类型(如自定义类、集合)时会抛出异常,需要实现深度拷贝,完成ClassA与ClassB中对应属性(包括集合)的拷贝。
解决方案
要实现支持非基元类型的深度拷贝,需要修复原代码的逻辑错误,并针对不同类型的属性做差异化处理,确保每个引用类型都创建新实例而非直接赋值引用。
1. 修复原代码核心错误
原代码中p.GetType() == targetObj.GetType()的判断完全无效——它比较的是PropertyInfo自身的类型(所有属性的PropertyInfo类型都是相同的),正确的判断应该是比较属性的实际类型:p.PropertyType == targetObj.PropertyType。
2. 完整深度拷贝实现
以下实现递归处理引用类型,同时单独处理集合类型,完成深度拷贝:
using System; using System.Collections; using System.Collections.Generic; using System.Reflection; public class DeepCopyHelper { public static void DeepCopy<T>(object source, ref T destination) { if (source == null || destination == null) return; Type sourceType = source.GetType(); Type targetType = destination.GetType(); foreach (PropertyInfo sourceProp in sourceType.GetProperties(BindingFlags.Public | BindingFlags.Instance)) { PropertyInfo targetProp = targetType.GetProperty(sourceProp.Name, BindingFlags.Public | BindingFlags.Instance); if (targetProp == null || !targetProp.CanWrite) continue; object sourceValue = sourceProp.GetValue(source); if (sourceValue == null) { targetProp.SetValue(destination, null); continue; } Type propType = sourceProp.PropertyType; // 值类型与字符串直接赋值(字符串不可变,无需深度拷贝) if (propType.IsValueType || propType == typeof(string)) { targetProp.SetValue(destination, sourceValue); } // 处理集合类型(如List<T>) else if (typeof(IEnumerable).IsAssignableFrom(propType) && propType != typeof(string)) { ICollection targetCollection = (ICollection)Activator.CreateInstance(targetProp.PropertyType); foreach (var item in (IEnumerable)sourceValue) { object newItem = Activator.CreateInstance(item.GetType()); DeepCopy(item, ref newItem); targetCollection.Add(newItem); } targetProp.SetValue(destination, targetCollection); } // 处理自定义引用类型 else { object targetValue = Activator.CreateInstance(targetProp.PropertyType); DeepCopy(sourceValue, ref targetValue); targetProp.SetValue(destination, targetValue); } } } }
3. 使用方式
// 初始化源对象 var sourceA = new A.ClassA { A = 10, B = "Demo", TypeA = new List<A.typeA> { new A.typeA(), new A.typeA() }, TypeB = new A.TypeB() }; // 创建目标对象实例 var targetB = new B.ClassB(); // 执行深度拷贝 DeepCopyHelper.DeepCopy(sourceA, ref targetB);
注意事项
- 要求所有自定义引用类型必须具备无参构造函数,如果需要支持有参构造,可以扩展代码添加构造函数参数匹配逻辑
- 如果存在循环引用(比如对象A引用对象B,对象B又引用对象A),需要额外添加缓存字典(如
Dictionary<object, object>)记录已拷贝的对象,避免无限递归 - 该实现仅处理公共实例属性,如需处理私有属性或字段,可调整
BindingFlags参数
内容的提问来源于stack exchange,提问作者Nodoid
相关产品推荐
相关产品推荐

