我想做一些事情,比如:
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();
然后对未反映在原始对象中的新对象进行更改。
我通常不需要这个功能,所以当有必要时,我会创建一个新对象,然后单独复制每个属性,但这总是让我觉得有更好或更优雅的方法来处理这种情况。
如何克隆或深度复制对象,以便在原始对象中不反映任何更改的情况下修改克隆的对象?
当前回答
如果您的对象树是可序列化的,您也可以使用以下内容
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;
}
请注意,此解决方案非常简单,但它不像其他解决方案那样高效。
请确保,如果类增长,仍然只有那些字段被克隆,这些字段也会被序列化。
其他回答
如果您的对象树是可序列化的,您也可以使用以下内容
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)
映射器执行深度复制。为对象的每个成员创建一个新对象并分配其所有值。它递归地处理每个非基元内部成员。
我建议您选择目前发展最快、最活跃的产品之一。我建议使用UltraMapperhttps://github.com/maurosampietro/UltraMapper
Nuget软件包:https://www.nuget.org/packages/UltraMapper/
我知道,这个问题和答案在这里停留了一段时间,下面不是很好的答案,而是观察,最近我在检查是否确实没有克隆隐私(如果没有,我就不会是我自己了;)时,我很高兴地复制了粘贴的@johnc更新的答案。
我简单地制作了自己的扩展方法(这几乎是上述答案的复制粘贴):
public static class CloneThroughJsonExtension
{
private static readonly JsonSerializerSettings DeserializeSettings = new JsonSerializerSettings { ObjectCreationHandling = ObjectCreationHandling.Replace };
public static T CloneThroughJson<T>(this T source)
{
return ReferenceEquals(source, null) ? default(T) : JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(source), DeserializeSettings);
}
}
并天真地放弃了这样的课程(事实上,有更多这样的课程,但它们是无关的):
public class WhatTheHeck
{
public string PrivateSet { get; private set; } // matches ctor param name
public string GetOnly { get; } // matches ctor param name
private readonly string _indirectField;
public string Indirect => $"Inception of: {_indirectField} "; // matches ctor param name
public string RealIndirectFieldVaule => _indirectField;
public WhatTheHeck(string privateSet, string getOnly, string indirect)
{
PrivateSet = privateSet;
GetOnly = getOnly;
_indirectField = indirect;
}
}
代码如下:
var clone = new WhatTheHeck("Private-Set-Prop cloned!", "Get-Only-Prop cloned!", "Indirect-Field clonned!").CloneThroughJson();
Console.WriteLine($"1. {clone.PrivateSet}");
Console.WriteLine($"2. {clone.GetOnly}");
Console.WriteLine($"3.1. {clone.Indirect}");
Console.WriteLine($"3.2. {clone.RealIndirectFieldVaule}");
结果是:
1. Private-Set-Prop cloned!
2. Get-Only-Prop cloned!
3.1. Inception of: Inception of: Indirect-Field cloned!
3.2. Inception of: Indirect-Field cloned!
我整个人都在想:什么……所以我抓起Newtonsoft.Json Github repo,开始挖掘。结果是:当反序列化一个恰好只有一个ctor且其参数名匹配(不区分大小写)公共属性名的类型时,它们将作为这些参数传递给ctor。在这里和这里的代码中可以找到一些线索。
要旨
我知道这不是很常见的情况,示例代码有点滥用,但嘿!当我检查灌木丛中是否有龙在等着跳出来咬我的屁股时,我大吃一惊;)
通常,您实现ICloneable接口并自己实现克隆。C#对象有一个内置的MemberwiseColone方法,该方法执行浅层复制,可以帮助您处理所有原语。
对于深度复制,它无法知道如何自动执行。