我想做一些事情,比如:
MyObject myObj = GetMyObj(); // Create and fill a new object
MyObject newObj = myObj.Clone();
然后对未反映在原始对象中的新对象进行更改。
我通常不需要这个功能,所以当有必要时,我会创建一个新对象,然后单独复制每个属性,但这总是让我觉得有更好或更优雅的方法来处理这种情况。
如何克隆或深度复制对象,以便在原始对象中不反映任何更改的情况下修改克隆的对象?
我想做一些事情,比如:
MyObject myObj = GetMyObj(); // Create and fill a new object
MyObject newObj = myObj.Clone();
然后对未反映在原始对象中的新对象进行更改。
我通常不需要这个功能,所以当有必要时,我会创建一个新对象,然后单独复制每个属性,但这总是让我觉得有更好或更优雅的方法来处理这种情况。
如何克隆或深度复制对象,以便在原始对象中不反映任何更改的情况下修改克隆的对象?
当前回答
在我使用的代码库中,我们有一个来自GitHub项目Burtsev Alexey/net对象深度副本的ObjectExtension.cs文件副本。它已经9岁了。虽然我们后来意识到,对于更大的对象结构来说,它是非常慢的,但它还是有效的。
相反,我们在GitHub项目jpmikkers/Baksteen.Extensions.DeepCopy中找到了ObjectExtension.cs文件的一个分支。以前对大型数据结构的深度复制操作需要大约30分钟,现在感觉几乎是瞬间完成的。
此改进版本包含以下文档:
用于快速对象克隆的C#扩展方法。这是Alexey Burtsev深度复印机的速度优化叉。根据您的使用情况,这将比原始版本快2倍-3倍。它还修复了原始代码中存在的一些错误。与经典的二进制序列化/反序列化深度克隆技术相比,此版本的速度大约快七倍(对象包含的数组越多,加速因子越大)。通过以下技术实现加速:缓存对象反射结果不要深度复制原语或不可变结构&类(例如枚举和字符串)为了提高引用的局部性,处理内部循环中的“快速”维度或多维数组使用编译的lambda表达式调用MemberwiseClone如何使用:使用Baksteen.Extensions.DeepCopy;...var myobject=新建SomeClass();...var myclone=myobject.DepCopy()!;//创建原始对象的新深度副本注意:只有在项目中启用了可为null的引用类型时,才需要感叹号(null原谅运算符)
其他回答
我提出这一点是为了克服.NET的一个缺点,即必须手动深度复制List<T>。
我使用这个:
static public IEnumerable<SpotPlacement> CloneList(List<SpotPlacement> spotPlacements)
{
foreach (SpotPlacement sp in spotPlacements)
{
yield return (SpotPlacement)sp.Clone();
}
}
在另一个地方:
public object Clone()
{
OrderItem newOrderItem = new OrderItem();
...
newOrderItem._exactPlacements.AddRange(SpotPlacement.CloneList(_exactPlacements));
...
return newOrderItem;
}
我试图提出一个这样做的oneliner,但这是不可能的,因为yield不能在匿名方法块中工作。
更好的是,使用通用的List<T>克隆器:
class Utility<T> where T : ICloneable
{
static public IEnumerable<T> CloneList(List<T> tl)
{
foreach (T t in tl)
{
yield return (T)t.Clone();
}
}
}
通常,您实现ICloneable接口并自己实现克隆。C#对象有一个内置的MemberwiseColone方法,该方法执行浅层复制,可以帮助您处理所有原语。
对于深度复制,它无法知道如何自动执行。
最短的方式,但需要依赖:
using Newtonsoft.Json;
public static T Clone<T>(T source) =>
JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(source));
如果您的对象树是可序列化的,您也可以使用以下内容
static public MyClass Clone(MyClass myClass)
{
MyClass clone;
XmlSerializer ser = new XmlSerializer(typeof(MyClass), _xmlAttributeOverrides);
using (var ms = new MemoryStream())
{
ser.Serialize(ms, myClass);
ms.Position = 0;
clone = (MyClass)ser.Deserialize(ms);
}
return clone;
}
请注意,此解决方案非常简单,但它不像其他解决方案那样高效。
请确保,如果类增长,仍然只有那些字段被克隆,这些字段也会被序列化。
DeepClonner:快速、简单、有效的NuGet包解决克隆问题
看完所有的答案后,我很惊讶没有人提到这个优秀的包:
DeepCloner GitHub项目
DeepCloner NuGet包
详细介绍一下它的README,下面是我们选择它的原因:
它可以深层或浅层复制在深度克隆中,维护所有对象图。在运行时使用代码生成,因为结果克隆非常快由内部结构复制的对象,未调用方法或构造函数您不需要以某种方式标记类(如Serializable属性或实现接口)无需为克隆指定对象类型。对象可以被强制转换为接口或抽象对象(例如,您可以将int数组克隆为抽象array或IEnumerable;甚至可以克隆null而不出错)克隆的对象没有任何能力确定他是克隆的(除非使用非常特定的方法)
用法:
var deepClone = new { Id = 1, Name = "222" }.DeepClone();
var shallowClone = new { Id = 1, Name = "222" }.ShallowClone();
性能:
README包含各种克隆库和方法的性能比较:DeepCloner性能。
要求:
.NET 4.0或更高版本或.NET Standard 1.3(.NET Core)需要完全信任权限集或反射权限(MemberAccess)