JsonSerializer深度克隆最简单但会丢失类型信息,需显式指定泛型类型;BinaryFormatter已废弃且不安全;MemberwiseClone仅为浅拷贝;第三方库如DeepClone依赖运行时代码生成,不适用于AOT。

用 JsonSerializer 深度克隆最简单,但要注意类型丢失
只要对象能被 JSON 序列化(即没有循环引用、不包含不可序列化成员如 FileStream 或委托),JsonSerializer 是最快上手的方案。它自动处理嵌套对象、集合、值类型和可空类型。
常见错误现象:JsonSerializer 默认不保留原始类型信息——比如源对象是 Dictionary<string, Person>,反序列化后变成 Dictionary<string, JsonElement>,强转会抛 InvalidCastException。
- 显式指定目标类型:用
JsonSerializer.Deserialize<T>(json, options),别依赖泛型推导 - 禁用属性忽略:确保
JsonSerializerOptions.IgnoreNullValues = false,否则 null 字段会被跳过 - 遇到
DateTimeOffset或decimal时,确认JsonSerializerOptions.NumberHandling设置合理,否则可能精度丢失
var options = new JsonSerializerOptions { WriteIndented = true };
var json = JsonSerializer.Serialize(original, options);
var clone = JsonSerializer.Deserialize<MyClass>(json, options);
用 BinaryFormatter?别用了,已废弃且不安全
.NET 5+ 中 BinaryFormatter 被标记为 [Obsolete],且默认禁用;它存在严重反序列化漏洞,微软明确建议停止使用。即使你还在维护 .NET Framework 项目,也不该在新代码里启用它。
常见错误现象:在 .NET 6+ 项目中直接调用 BinaryFormatter.Deserialize 会触发运行时异常 NotSupportedException: BinaryFormatter serialization is disabled,不是编译错误,容易漏测。
- 不要试图通过
AppContext.SetSwitch("System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization", true)绕过——这只是临时补丁,不是解决方案 - 如果必须兼容旧二进制格式,改用
System.Text.Json或MessagePack重写序列化层 - 所有标记了
[Serializable]的类,都应同步加[JsonSerializable](.NET 7+)或适配JsonConverter
需要保留引用相等性?用 MemberwiseClone + 手动递归不现实
MemberwiseClone 是浅拷贝,对引用字段只复制地址。想靠它+递归实现深度克隆,等于自己重写序列化器——要处理循环引用检测、泛型约束、只读字段、IDisposable 成员释放、线程安全等,实际项目中没人这么干。
使用场景极少:仅限极简 POCO、无集合、无继承、无接口、所有字段都是值类型或字符串。
- 一旦类里有
List<T>或自定义引用类型字段,MemberwiseClone后修改副本会污染原对象 - 无法跨程序集克隆(
MemberwiseClone是protected,子类才能调) - 性能未必比序列化快——现代 JSON 库(如
System.Text.Json)已高度优化,小对象差异可忽略
第三方库选 DeepClone 还是 FastDeepCloner?看是否允许生成动态代码
这两个库都基于表达式树或 IL 生成做运行时克隆,不依赖序列化,能保留类型、引用关系甚至部分不可序列化成员(如事件委托)。但它们的行为边界很关键。
常见错误现象:FastDeepCloner 默认跳过 private set 属性和只读字段(readonly / init),而 DeepClone 需显式配置 IncludeNonPublic 才能访问私有字段。
- 若对象含
Expression<Func<T>>或DynamicObject,两个库都会失败,得降级到手动映射 -
DeepClone支持CloneSettings控制字段粒度,适合混合策略(如某些字段跳过、某些字段深拷) - 注意 AOT 编译限制:.NET 6+ 的 NativeAOT 不支持运行时 IL 生成,这类库直接不可用










