从翻车到「版本答案」

📅 2026/6/26 2:48:35
从翻车到「版本答案」
前言几年前写过一个 bug根因很土该深拷贝的地方没深拷贝副本一改原件跟着变。排查的时候老板以为动的是库里的数据其实就是一个本地对象被共享了。先把词说清楚浅拷贝值类型复制一份引用类型复制的是引用两边还指着同一个子对象。你改副本里的引用成员原件也会变。只复制对象自身的一层字段/属性里如果是值类型会复制一份值如果是引用类型复制的是引用指针新旧对象仍指向同一块堆上的子对象。深拷贝引用链上也建新对象改副本不该动到原件的嵌套数据。从根对象开始递归地为引用类型也创建新实例并把内容复制过去直到整棵「对象图」在逻辑上独立。改拷贝不应意外改动原对象里的嵌套数据。ICloneable能深但接口不保证ICloneable只有一个object Clone()文档不会替你承诺浅还是深看实现。你想做深拷贝可以全写在Clone()里就行。浅拷贝场景下改拷贝里的引用类型字段往往会影响原对象反之亦然除非你再给那个字段赋一个新实例。// 浅拷贝示例Address 还是同一个引用 public class DeepAndShallowCopy { public static void ShallowCopy() { var rawUser new UserDto { Id 1, Name name1, Address new UserDto.AddressDto { City CS } }; var copyUser rawUser.Clone() as UserDto; copyUser.Id 2; copyUser.Name name2; copyUser.Address.City CS2; // 浅拷贝动的是同一块 Address原数据跟着变 Console.WriteLine($rawUser{JsonSerializer.Serialize(rawUser)}); Console.WriteLine($copyUser{JsonSerializer.Serialize(copyUser)}); } } public class UserDto : ICloneable { public int Id { get; set; } public string Name { get; set; } public AddressDto Address { get; set; } public object Clone() { return new UserDto { Id Id, Name Name, Address Address }; } public class AddressDto { public string City { get; set; } } }深拷贝就要让Address也Clone()一份。引用类型多就一层层写啰嗦但清楚。public class DeepAndShallowCopy { public static void DeepCopy() { var rawUser new UserDto { Id 1, Name name1, Address new UserDto.AddressDto { City CS } }; var copyUser rawUser.Clone() as UserDto; copyUser.Id 2; copyUser.Name name2; copyUser.Address.City CS2; // 深拷贝Address 已是新实例原数据不变 Console.WriteLine($rawUser{JsonSerializer.Serialize(rawUser)}); Console.WriteLine($copyUser{JsonSerializer.Serialize(copyUser)}); } } public class UserDto : ICloneable { public int Id { get; set; } public string Name { get; set; } public AddressDto Address { get; set; } public object Clone() { return new UserDto { Id Id, Name Name, Address Address.Clone() as AddressDto }; } public class AddressDto : ICloneable { public string City { get; set; } public object Clone() { return new AddressDto { City City }; } } }手写这条路性能好行为自己说了算。代价是对象图一大就容易漏漏一处就是浅拷贝另外Clone()返回object调用处总要转一下类型有点烦。序列化 / AutoMapper省事但要心里有数我们 CRUD 程序员经常不想维护一整张克隆图就会想走捷径。System.Text.Json思路就是序列化再反序列化得到一棵新对象。代码少DTO、配置这类能完整序列化的类型用起来很省事。public class DeepAndShallowCopy