2021年C#不可序列化对象深拷贝:无源码访问权限如何实现?
C# 无源码无序列化能力场景下的深拷贝实现方案
结论
可以实现。不需要修改目标对象源码,也不需要依赖序列化能力,核心实现思路基于反射+递归遍历+循环引用检测,可以覆盖所有访问修饰符的字段、跨程序集成员、多层嵌套引用场景。
核心实现步骤
- 循环引用检测:提前定义全局字典
Dictionary<object, object> _copiedObjects存储已拷贝的对象映射,键为原对象,值为已生成的拷贝对象,避免多层嵌套时出现循环引用导致无限递归,同时避免重复拷贝相同对象。 - 实例化新对象:
- 值类型和
string类型直接返回原对象,无引用拷贝风险 - 数组类型先创建相同长度、相同类型的新数组,后续递归拷贝每个元素
- 普通引用类型用
Activator.CreateInstance(type, true)调用无参构造创建实例,支持调用private修饰的构造函数 - 遇到无无参构造函数的特殊类型,可改用
FormatterServices.GetUninitializedObject(type)创建未初始化的对象,无需触发构造函数
新实例创建完成后先存入_copiedObjects字典,再进行后续字段拷贝。
- 值类型和
- 全量字段拷贝:通过反射获取类型所有字段,绑定标志设置为
BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy,可覆盖private、protected、internal修饰的所有实例字段,包括父类继承的非公开字段。遍历每个字段递归执行拷贝逻辑,再赋值给新实例对应字段。 - 边界处理:委托类型可根据需求选择直接浅拷贝赋值或者置空;非托管指针类型需要结合实际业务场景判断是否需要额外拷贝逻辑。
核心代码示例
public static class DeepCopier { private static readonly Dictionary<object, object> _copiedMap = new Dictionary<object, object>(); public static T DeepCopy<T>(T original) { if (original == null) return default; Type type = original.GetType(); // 处理值类型和字符串 if (type.IsValueType || type == typeof(string)) { return original; } // 已拷贝过的对象直接返回,避免循环引用 if (_copiedMap.ContainsKey(original)) { return (T)_copiedMap[original]; } // 处理数组类型 if (type.IsArray) { var elementType = type.GetElementType(); var originalArr = original as Array; var newArr = Array.CreateInstance(elementType, originalArr.Length); _copiedMap.Add(original, newArr); for (int i = 0; i < originalArr.Length; i++) { newArr.SetValue(DeepCopy(originalArr.GetValue(i)), i); } return (T)(object)newArr; } // 创建普通引用类型实例 object copy = Activator.CreateInstance(type, true); _copiedMap.Add(original, copy); // 拷贝所有访问级别的字段 var fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy); foreach (var field in fields) { var fieldVal = field.GetValue(original); if (fieldVal == null) { field.SetValue(copy, null); continue; } field.SetValue(copy, DeepCopy(fieldVal)); } // 清空映射字典,避免影响下次调用 _copiedMap.Clear(); return (T)copy; } }
注意事项
- 部分高权限限制的运行环境(如沙箱)可能会限制反射访问非公开成员,需提前确认运行环境权限。
- 该方案性能略低于序列化方案,适合对性能要求不是极致苛刻的场景。
内容的提问来源于stack exchange,提问作者Beep13
相关产品推荐
相关产品推荐

