我想做的事情如下:
MyObject myObj = GetMyObj(); // Create and fill a new object
MyObject newObj = myObj.Clone();
然后更改未在原始对象中反映的新对象。
我不经常需要这个功能,所以当有必要的时候,我已经使用了创建一个新对象然后单独复制每个属性,但它总是让我觉得有更好或更优雅的处理方式情况。
如何克隆或深度复制对象,以便可以修改克隆的对象而不会在原始对象中反映任何更改?
虽然标准做法是实现ICloneable
接口( 这里描述,所以我不会反刍),这是一个很好的深度克隆对象复印机,我在前一段时间在代码项目中找到并将其合并到我们的东西中。
正如其他地方所提到的,它确实需要您的对象可序列化。
using System;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
/// <summary>
/// Reference Article http://www.codeproject.com/KB/tips/SerializedObjectCloner.aspx
/// Provides a method for performing a deep copy of an object.
/// Binary Serialization is used to perform the copy.
/// </summary>
public static class ObjectCopier
{
/// <summary>
/// Perform a deep Copy of the object.
/// </summary>
/// <typeparam name="T">The type of object being copied.</typeparam>
/// <param name="source">The object instance to copy.</param>
/// <returns>The copied object.</returns>
public static T Clone<T>(T source)
{
if (!typeof(T).IsSerializable)
{
throw new ArgumentException("The type must be serializable.", "source");
}
// Don't serialize a null object, simply return the default for that object
if (Object.ReferenceEquals(source, null))
{
return default(T);
}
IFormatter formatter = new BinaryFormatter();
Stream stream = new MemoryStream();
using (stream)
{
formatter.Serialize(stream, source);
stream.Seek(0, SeekOrigin.Begin);
return (T)formatter.Deserialize(stream);
}
}
}
这个想法是它序列化你的对象,然后将它反序列化为一个新的对象。好处是,当对象过于复杂时,您不必担心克隆所有内容。
并使用扩展方法(也来自最初引用的源):
如果您更喜欢使用 C#3.0 的新扩展方法 ,请将方法更改为具有以下签名:
public static T Clone<T>(this T source)
{
//...
}
现在方法调用只是变成objectBeingCloned.Clone();
。
编辑 (2015 年 1 月 10 日)以为我会重新审视这一点,提到我最近开始使用(Newtonsoft)Json 这样做,它应该更轻,并避免 [Serializable] 标签的开销。 ( 注意 @atconway 在评论中指出私有成员不是使用 JSON 方法克隆的)
/// <summary>
/// Perform a deep Copy of the object, using Json as a serialisation method. NOTE: Private members are not cloned using this method.
/// </summary>
/// <typeparam name="T">The type of object being copied.</typeparam>
/// <param name="source">The object instance to copy.</param>
/// <returns>The copied object.</returns>
public static T CloneJson<T>(this T source)
{
// Don't serialize a null object, simply return the default for that object
if (Object.ReferenceEquals(source, null))
{
return default(T);
}
// initialize inner objects individually
// for example in default constructor some list property initialized with some values,
// but in 'source' these items are cleaned -
// without ObjectCreationHandling.Replace default constructor values will be added to result
var deserializeSettings = new JsonSerializerSettings {ObjectCreationHandling = ObjectCreationHandling.Replace};
return JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(source), deserializeSettings);
}
我想要一个非常简单的对象,主要是原始和列表的克隆人。如果你的对象是开箱即用的 JSON serializable,那么这个方法就可以了。这不需要修改或实现克隆类上的接口,只需要像 JSON.NET 这样的 JSON 序列化程序。
public static T Clone<T>(T source)
{
var serialized = JsonConvert.SerializeObject(source);
return JsonConvert.DeserializeObject<T>(serialized);
}
此外,您可以使用此扩展方法
public static class SystemExtension
{
public static T Clone<T>(this T source)
{
var serialized = JsonConvert.SerializeObject(source);
return JsonConvert.DeserializeObject<T>(serialized);
}
}
不使用的原因ICloneable是不是因为它没有一个通用的接口。 不使用它的原因是因为它含糊不清 。它不清楚你是否得到浅拷贝或深拷贝; 这取决于实施者。
是的, MemberwiseClone
制作浅层副本,但与MemberwiseClone
相反的不是Clone
; 也许是DeepClone
,它不存在。通过其 ICloneable 接口使用对象时,您无法知道底层对象执行哪种克隆。 (并且 XML 注释不会说清楚,因为您将获得接口注释而不是对象的 Clone 方法上的注释。)
我通常做的只是制作一个完全符合我想要的Copy
方法。