JsonSerializer 最快最稳,但要注意类型丢失;MemberwiseClone 是浅拷贝,对 List 无效;自定义 ICloneable 需 T 支持且性能较差;第三方库如 DeepCloner 虽方便但有 AOT 和调试风险。

用 JsonSerializer 最快最稳,但要注意类型丢失
直接序列化再反序列化是目前 C# 里最省心的深拷贝方式,尤其适合 List 这类简单集合。.NET 6+ 自带 System.Text.Json,不用装包,性能也比 Newtonsoft.Json 更好。
常见错误是把 DateTime、Dictionary 或含循环引用的对象硬塞进去,结果抛出 NotSupportedException 或字段为空。
- 只对 public 字段/属性生效,private 或 [JsonIgnore] 的不会拷过去
- 接口类型(如
IList)反序列化后变成具体类型(List),原类型信息丢了 - 如果
T里有不可序列化的成员(比如委托、指针、FileStream),会直接失败
var original = new List<person> { new Person { Name = "Alice", Age = 30 } };
var clone = JsonSerializer.Deserialize<list>>(
JsonSerializer.Serialize(original));</list></person>
MemberwiseClone 不行,别试了
MemberwiseClone 是浅拷贝,对 List 完全无效——它只复制引用,不复制元素本身。哪怕 T 是 class,新 list 里的每个对象还是指向老地址。
典型现象:改 clone 里的某个 Person.Name,original 里对应项也跟着变。
- 值类型(
int、struct)看起来“像深拷贝”,只是因为值被复制了,不是行为变了 - 只要
T是引用类型,MemberwiseClone就不满足“深”这个要求 - 它连 list 的容量、Count 都一并复制,但没意义——底层数组引用还是同一个
自定义 ICloneable + 循环拷贝,可控但累
如果你需要精确控制每个字段怎么拷、要保留接口类型、或得处理不可序列化字段,那就得手动写。核心就是 new 一个新 list,再对每个元素调用其自己的克隆逻辑。
关键点不在 list,而在 T 是否支持克隆。如果 T 没实现 ICloneable 或没提供拷贝构造函数,这条路就卡住。
- 别在循环里用
list.Add(item.Clone())然后假设Clone()一定返回正确类型——得强制转型或用泛型约束 - 注意空值:如果
item是 null,Clone()会崩,得提前判 - 性能上不如 JSON 方案,特别是大 list,GC 压力更明显
public static List<t> DeepClone<t>(this List<t> source) where T : ICloneable
{
var result = new List<t>(source.Count);
foreach (var item in source)
result.Add((T)item.Clone());
return result;
}</t></t></t></t>
第三方库(如 DeepCloner)能省事,但引入新风险
DeepCloner(NuGet 包)确实能一行搞定:list.DeepClone(),连循环引用、泛型、接口都自动处理。但它依赖反射和动态代码生成,运行时行为不够透明。
容易踩的坑是版本兼容性——.NET 8 下某些旧版 DeepCloner 会因 AOT 编译失败;另外,它默认跳过 readonly 字段,除非显式配置。
- 调试困难:出错时堆栈指向内部 emit 代码,看不出哪一行数据导致问题
- 发布到 AOT 或 ILTrim 环境前必须验证,否则可能 runtime missing method
- 如果项目已用
System.Text.Json,为深拷贝再加一个重量级依赖,有点重
深拷贝真正的复杂点从来不在 list 本身,而在于你是否清楚 T 的结构——它有没有隐藏引用?有没有非序列化字段?要不要保持原有接口契约?这些决定了你该停在哪一层。










