我想做一些事情,比如:
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();
然后对未反映在原始对象中的新对象进行更改。
我通常不需要这个功能,所以当有必要时,我会创建一个新对象,然后单独复制每个属性,但这总是让我觉得有更好或更优雅的方法来处理这种情况。
如何克隆或深度复制对象,以便在原始对象中不反映任何更改的情况下修改克隆的对象?
当前回答
使用System.Text.Json:
https://devblogs.microsoft.com/dotnet/try-the-new-system-text-json-apis/
public static T DeepCopy<T>(this T source)
{
return source == null ? default : JsonSerializer.Parse<T>(JsonSerializer.ToString(source));
}
新的API使用Span<T>。这应该很快,最好做一些基准测试。
注意:不需要像Json.NET那样使用ObjectCreationHandling。Replace,因为默认情况下它将替换集合值。您现在应该忘记Json.NET,因为所有的东西都将被新的官方API所取代。
我不确定这是否适用于私人领域。
其他回答
由于这个问题的几乎所有答案都不令人满意,或者在我的情况下显然不起作用,我编写了AnyClone,它完全通过反思实现,并解决了这里的所有需求。我无法让序列化在结构复杂的复杂场景中工作,IClonable也不太理想——事实上,它甚至没有必要。
使用[IgnoreDataMember]、[NonSerialized]支持标准忽略属性。支持复杂集合、不带setter的财产、只读字段等。
我希望这能帮助其他遇到我同样问题的人。
下面是一个深度拷贝实现:
public static object CloneObject(object opSource)
{
//grab the type and create a new instance of that type
Type opSourceType = opSource.GetType();
object opTarget = CreateInstanceOfType(opSourceType);
//grab the properties
PropertyInfo[] opPropertyInfo = opSourceType.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
//iterate over the properties and if it has a 'set' method assign it from the source TO the target
foreach (PropertyInfo item in opPropertyInfo)
{
if (item.CanWrite)
{
//value types can simply be 'set'
if (item.PropertyType.IsValueType || item.PropertyType.IsEnum || item.PropertyType.Equals(typeof(System.String)))
{
item.SetValue(opTarget, item.GetValue(opSource, null), null);
}
//object/complex types need to recursively call this method until the end of the tree is reached
else
{
object opPropertyValue = item.GetValue(opSource, null);
if (opPropertyValue == null)
{
item.SetValue(opTarget, null, null);
}
else
{
item.SetValue(opTarget, CloneObject(opPropertyValue), null);
}
}
}
}
//return the new item
return opTarget;
}
这个方法为我解决了问题:
private static MyObj DeepCopy(MyObj source)
{
var DeserializeSettings = new JsonSerializerSettings { ObjectCreationHandling = ObjectCreationHandling.Replace };
return JsonConvert.DeserializeObject<MyObj >(JsonConvert.SerializeObject(source), DeserializeSettings);
}
这样使用:MyObj a=DeepCopy(b);
简单的答案是从ICloneable接口继承,然后实现.clone函数。克隆应该按成员进行复制,并对任何需要它的成员执行深度复制,然后返回结果对象。这是一个递归操作(它要求您要克隆的类的所有成员都是值类型或实现ICloneable,并且它们的成员是值类型或者实现IClonea,依此类推)。
有关使用ICloneable克隆的详细说明,请参阅本文。
长篇大论的答案是“视情况而定”。正如其他人所提到的,ICloneable不受泛型支持,需要对循环类引用进行特殊考虑,并且实际上被一些人视为.NET Framework中的“错误”。序列化方法取决于对象是否可序列化,而这些对象可能不可序列化,并且您可能无法控制。对于哪种做法是“最佳”做法,社会上仍有很多争论。事实上,没有一个解决方案是适用于所有情况的一刀切的最佳实践,就像ICloneable最初被解释的那样。
请参阅本开发者角文章,了解更多选项(归功于Ian)。
遵循以下步骤:
使用返回T的只读Self属性和ICloneable<out T>定义ISelf<T>,ICloneale<out T<派生自ISelf>并包含方法T Clone()。然后定义一个CloneBase类型,该类型实现了一个受保护的虚拟泛型VirtualClone,将MemberwiseClone转换为传入的类型。每个派生类型都应该通过调用基本克隆方法来实现VirtualClone,然后执行所需的操作来正确克隆父VirtualClone方法尚未处理的派生类型的那些方面。
为了实现最大的继承通用性,公开公共克隆功能的类应该是密封的,但派生自一个基类,该基类在其他方面是相同的,除非缺少克隆。不要传递显式可克隆类型的变量,而是采用ICloneable<theNonCloneableType>类型的参数。这将允许期望Foo的可克隆衍生物与DerivedFoo的可克隆衍生物一起工作的例程,但也允许创建Foo的不可克隆衍生物。