memberwiseclone适用于浅拷贝,仅复制顶层字段引用;深拷贝推荐system.text.json序列化,避免已废弃且不安全的binaryformatter,注意json化限制与陷阱。

浅拷贝用 MemberwiseClone 就够了,深拷贝别直接上序列化——它会咬你一口。
什么时候该用 MemberwiseClone
当你只关心对象顶层字段的“表面复制”,且内部引用类型字段不需要隔离时,MemberwiseClone 是最轻量、最安全的选择。它不依赖任何特性标记,也不抛 SerializationException,性能也比序列化快一个数量级。
- 值类型字段(
int、DateTime、struct)会被完整复制,改副本不影响原对象 - 引用类型字段(如
string、List<t></t>、自定义类)只复制引用地址,新旧对象仍共享同一实例 - 必须声明为
protected或public方法调用,不能直接在外部 new 一个对象后赋值替代——那只是引用传递,不是拷贝
示例:Student 类含 Grade 引用字段,调用 ShallowCopy() 后修改 copy.Grade.Stu_Grade,source.Grade.Stu_Grade 也会变。
为什么 BinaryFormatter 深拷贝已不推荐
它曾是 .NET Framework 时代最常用的深拷贝手段,但自 .NET Core 3.0 起被标记为 Obsolete,.NET 5+ 默认禁用,且存在严重反序列化漏洞风险。即使你加了 [Serializable],运行时也可能抛出 NotSupportedException 或拒绝反序列化。
- 必须给所有嵌套类型都打
[Serializable],漏一个就崩 - 不能处理
ISerializable自定义逻辑、readonly字段、IntPtr、委托、事件等 - 跨平台(如 Linux 上的 .NET 6+)默认禁用,需手动启用
UnsafeDeserialize,等于主动开后门
别再搜到老博客就抄 BinaryFormatter 示例——它现在是技术债,不是解决方案。
真正可用的深拷贝方案:JSON 序列化 + System.Text.Json
这是目前 .NET 5+ 最实用、最可控的深拷贝路径,前提是你的对象可 JSON 化(无循环引用、无不可序列化成员)。
- 用
JsonSerializer.SerializeToUtf8Bytes+JsonSerializer.Deserialize<t></t>组合,避免字符串中间态,减少 GC 压力 - 需注意:默认不支持
DateTimeKind.Unspecified的精度保留、Dictionary<k></k>的键类型限制、以及private set属性是否参与——可通过JsonSerializerOptions控制 - 对含
byte[]、Stream、IDisposable成员的对象,必须提前清理或忽略,否则会抛NotSupportedException
示例代码片段:
var options = new JsonSerializerOptions { WriteIndented = false, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull };
var bytes = JsonSerializer.SerializeToUtf8Bytes(original, options);
var clone = JsonSerializer.Deserialize<myclass>(bytes, options);</myclass>
容易被忽略的深拷贝陷阱
深拷贝不是“只要跑通就行”的操作。很多看似成功的克隆,在真实业务中会突然失效。
-
string字段在浅拷贝里表现像值类型(因不可变性),但若你用StringBuilder或char[],行为立刻不同——别凭直觉判断“它应该没问题” - 实现
IDisposable的对象(如含文件句柄、数据库连接)不能靠序列化深拷贝,必须重写Clone手动管理资源 - 属性有
get无set(只读自动属性)时,System.Text.Json默认不反序列化,得配PropertyNameCaseInsensitive = true和IncludeFields = true等选项 - 多线程环境下,如果源对象正在被修改,而你同时做深拷贝,结果可能处于中间状态——深拷贝不解决并发一致性问题
真正要拷贝的对象越复杂,越该放弃通用序列化,转而实现显式的 Clone 方法,哪怕多写几行。











