CSharp - 深层克隆的对象

  显示原文与译文双语对照的内容

我想做一些类似的事情:


myObject myObj = GetmyObj();//Create and fill a new object
myObject newObj = myObj.Clone();

然后对未反映在原始对象中的新对象进行更改。

我不经常需要这个功能,所以当需要时,我不得不创建一个新的对象,然后逐个复制每个属性,但它总是让我感觉到一种更好或者更优雅的方式来处理这种情况。

如何克隆或者复制一个对象,以便克隆的对象可以被修改,而不会在原始对象中反映任何更改?

时间:

在我们 stuff,,虽然该标准的做法是以实现该 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();

编辑 ( 10年 2015月) 就想重温这个,要提到我最近开始使用( Newtonsoft ) Json要执行这里操作,它 [Serializable] 应较轻,且避免了开销。标签。 使用JSON方法 ), ( @atconway 有在评论中指出,私有成员不被克隆


///<summary>
///Perform a deep Copy of the object, using Json as a serialisation 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);
 }

 return JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(source));
}

以下是我过去使用过的一个不错的资源: C# 对象克隆

不要使用ICloneable是的原因,因为它没有一个泛型接口。 不使用它的原因是它是模糊的 。 它不说清楚你是否去看看,有浅或者深复制,也就是说最多实现的实例。

是的,MemberwiseCloneMemberwiseClone 使得一个浅表副本,但相反的,不是 Clone ;这将是,也许,DeepClone,它不存在。 当你通过它的ICloneable接口使用对象时,你无法知道基础对象执行哪种克隆。 ( 和XML注释不会使它的清晰,因为你将获得接口注释,而不是对象方法的克隆上的注释。)

我通常做的只是做一个 Copy 方法来完成我想要的。

我想要克隆一个非常简单的对象,主要是基元和列表。 如果你的对象超出了JSON可以序列化的范围,那么这个方法就会执行这个操作。 这不需要在克隆类上修改或者实现接口,就像 JSON.NET. 这样的一个JSON序列化程序


public static T Clone<T>(T source)
{
 var serialized = JsonConvert.SerializeObject(source);
 return JsonConvert.DeserializeObject<T>(serialized);
}

我更喜欢复制构造函数到克隆。 意图更清晰。

comments,方言的问题经过大量的阅读量有关的许多选项链接在这里,对这里问题,并提出改进建议,我相信所有的选择都已经总结出基本准确的伊恩P 环节 ( 所有其他选项都是这些) 和所提供的最好的解决方案是 Pedro77. 链接。

所以我将在这里复制这些 2引用的相关部分。 这样我们就可以:

最好的方法是在sharp中克隆对象 !

首先,最重要的是,这些都是我们的选择:

我为什么选择 IClonable ( 例如 ) 。 手动)

先生 Venkat Subramaniam ( 这里的冗余链接) 详细解释了为什么

使用 3 objects,他所有文章一直绕一个示例,该示例试图成为适用于大多数情况下, : 人,大脑和城市 。 我们想要克隆一个拥有自己的大脑但同样的城市的人。 你可以选择图片上面存在问题的任何其他方法可以导致或者阅读这篇文章。

这是我对他的结论稍加修改的版本:

通过指定 New 和类名来复制对象,通常会导致代码不可扩展。 使用克隆,Prototype模式的应用,是一种更好的实现方法。 但是,在 C# ( 和 Java ) 中使用克隆也会有很大的问题。 最好提供受保护的( 非公共) 复制构造函数,并从克隆方法调用它。 这个使我们有能力在constructor,来将创建对象的任务委托给子类使用受保护副本类的实例本身,从而提供可以扩展性,另外,安全地创建了 objects.

希望这个实现能让事情变得清晰:


public class Person implements Cloneable
{
 private final Brain brain;//brain is final since I do not want 
//any transplant on it once created!
 private int age;
 public Person(Brain aBrain, int theAge)
 {
 brain = aBrain; 
 age = theAge;
 }
 protected Person(Person another)
 {
 Brain refBrain = null;
 try
 {
 refBrain = (Brain) another.brain.clone();
//You can set the brain in the constructor
 }
 catch(CloneNotSupportedException e) {}
 brain = refBrain;
 age = another.age;
 }
 public String toString()
 {
 return"This is person with" + brain;
//Not meant to sound rude as it reads!
 }
 public Object clone()
 {
 return new Person(this);
 }
 …
}

现在考虑让一个类从Person派生。


public class SkilledPerson extends Person
{
 private String theSkills;
 public SkilledPerson(Brain aBrain, int theAge, String skills)
 {
 super(aBrain, theAge);
 theSkills = skills;
 }
 protected SkilledPerson(SkilledPerson another)
 {
 super(another);
 theSkills = another.theSkills;
 }

 public Object clone()
 {
 return new SkilledPerson(this);
 }
 public String toString()
 {
 return"SkilledPerson:" + super.toString();
 }
}

你可以尝试运行以下代码:


public class User
{
 public static void play(Person p)
 {
 Person another = (Person) p.clone();
 System.out.println(p);
 System.out.println(another);
 }
 public static void main(String[] args)
 {
 Person sam = new Person(new Brain(), 1);
 play(sam);
 SkilledPerson bob = new SkilledPerson(new SmarterBrain(), 1,"Writer");
 play(bob);
 }
}

生成的输出将是:


This is person with Brain@1fcc69
This is person with Brain@253498
SkilledPerson: This is person with SmarterBrain@1fef6f
SkilledPerson: This is person with SmarterBrain@209f4e

观察到,如果我们的对象的数量的计数里有,这里实现的克隆作为将保持一个正确的对象的数量的计数。

复制所有公共属性的简单扩展方法。 适合其他对象和需要允许 [Serializable] 此类,。 可以扩展为其他访问级别。


public static void CopyTo( this object S, object T )
{
 foreach( var pS in S.GetType().GetProperties() )
 {
 foreach( var pT in T.GetType().GetProperties() )
 {
 if( pT.Name!= pS.Name ) continue;
 ( pT.GetSetMethod() ).Invoke( T, new object[] { pS.GetGetMethod().Invoke( S, null ) } );
 }
 };
}

我在Silverlight中使用ICloneable时遇到问题,但是我喜欢seralization的思想,我可以 seralize,所以我这样做:


static public class SerializeHelper
{
//Michael White, Holly Springs Consulting, 2009
//michael@hollyspringsconsulting.com

 public static T DeserializeXML<T>(string xmlData)
 where T:new()
 {
 if (string.IsNullOrEmpty(xmlData))
 return default(T);

 TextReader tr = new StringReader(xmlData);
 T DocItms = new T();
 XmlSerializer xms = new XmlSerializer(DocItms.GetType());
 DocItms = (T)xms.Deserialize(tr);

 return DocItms == null? default(T) : DocItms;
 }

 public static string SeralizeObjectToXML<T>(T xmlObject)
 {
 StringBuilder sbTR = new StringBuilder();
 XmlSerializer xmsTR = new XmlSerializer(xmlObject.GetType());
 XmlWriterSettings xwsTR = new XmlWriterSettings();

 XmlWriter xmwTR = XmlWriter.Create(sbTR, xwsTR);
 xmsTR.Serialize(xmwTR,xmlObject);

 return sbTR.ToString();
 }

 public static T CloneObject<T>(T objClone)
 where T:new()
 {
 string GetString = SerializeHelper.SeralizeObjectToXML<T>(objClone);

 return SerializeHelper.DeserializeXML<T>(GetString);
 }



}

如果你已经使用了诸如 ValueInjecter 或者 Automapper这样的3参与方应用程序,你可以执行如下操作:


MyObject oldObj;//The existing object to clone

MyObject newObj = new MyObject();
newObj.InjectFrom(oldObj);//Using ValueInjecter syntax

使用这里方法,你不必在对象上实现ISerializable或者 ICloneable 。 这与 mvc/mvvm模式很常见,所以已经创建了这样简单的工具。

在这里查看ValueInjecter深度克隆解决方案: http://valueinjecter.codeplex.com/wikipage?title=Faster%20Deep%20Cloning%20using%20SmartConventionInjection%20and%20FastMember&referringTitle=Home

简短答案是从ICloneable接口继承,然后实现. clone 函数。 克隆应该执行memberwise复制并对需要它的任何成员执行深层复制,然后返回结果对象。 这是一个递归操作( 它要求你要克隆的类的所有成员都是值类型或者实现 ICloneable,它们的成员是值类型或者实现 ICloneable,等等。) 。

有关使用ICloneable进行克隆的更详细的说明,请查看本文:

http://www.ondotnet.com/pub dotnet/1 2002/11/25 1/copying. html

长的答案就是"这取决于"。 在. NET framework,由他人就像前面介绍的一样,通过泛型,需要特殊的注意事项不支持 ICloneable,圆形的类引用,实际上是由一些作为查看"错误". 序列化方法依赖于序列化的对象,它们可能不存在,并且你可能无法控制。 社区中仍然有很多争论,即"最佳"实践。 实际上,没有一个解决方案是one-size适合所有情况的最佳实践,比如ICloneable最初被解释为。

查看开发人员文章的这个角落,了解更多选项( 给Ian的信用): http://developerscon.blogspot.com/2008/06/c-object-clone-wars.html

...