深入克隆对象
我想做一些事情:
MyObject myObj = GetMyObj(); // Create and fill a new object MyObject newObj = myObj.Clone();
然后对未反映在原始对象中的新对象进行更改。
我不经常需要这个function,所以当需要的时候,我已经开始创build一个新的对象,然后单独地复制每个属性,但是总是让我觉得有一种更好或者更优雅的处理方式情况。
如何克隆或深度复制一个对象,以便可以修改克隆的对象,而不会在原始对象中反映任何更改?
虽然标准的做法是实现ICloneable
接口( 在这里描述,所以我不会反胃),这里有一个很好的深层克隆对象复制器,我在Code Project上find了,并将它合并到我们的东西中。
如别处所述,它确实需要您的对象是可序列化的。
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日)以为我会重温这个,提到我最近开始使用(牛顿软件)Json做到这一点,它应该更轻,并避免[Serializable]标签的开销。 ( NB @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可序列化的盒子,那么这个方法将做的伎俩。 这不需要修改或实现克隆的类上的接口,只需要像JSON.NET这样的JSON序列化器。
public static T Clone<T>(T source) { var serialized = JsonConvert.SerializeObject(source); return JsonConvert.DeserializeObject<T>(serialized); }
不使用ICloneable的原因不是因为它没有通用接口。 不使用它的原因是因为它是模糊的 。 它并不清楚你是否得到一个浅或深的副本; 这取决于实施者。
是的, MemberwiseClone
创build一个浅拷贝,但是MemberwiseClone
的对面不是Clone
; 它可能是DeepClone
,它不存在。 当您通过ICloneable接口使用对象时,无法知道底层对象执行哪种克隆。 (XML注释不会清楚,因为您将获得接口注释而不是对象的Clone方法。)
我通常做的只是做一个Copy
方法,正是我想要的。
我更喜欢复制构造函数克隆。 意图更清楚。
在阅读了很多与这里链接的选项以及这个问题的可能解决scheme之后,我相信在Ian P的链接 (所有其他选项都是这些的变体)中, 所有的选项都可以很好地总结出来 ,最好的解决scheme是由Pedro77的问题评论的链接 。
所以我只是在这里复制这两个引用的相关部分。 这样我们可以有:
在c sharp中克隆对象最好的办法是!
首先,这些都是我们的select:
- 手动与ICloneable ,这是浅而不是types安全的
- MemberwiseClone ,它使用ICloneable
- 通过使用Activator.CreateInstance进行 reflection
- 序列化 ,正如johnc的首选答案所指出的那样
- 中级语言 ,我不知道如何工作
- 扩展方法 ,例如Havard Straden定制的克隆框架
为什么我selectICloneable (即手动)
Venkat Subramaniam先生(冗余链接)详细解释了为什么 。
他的所有文章围绕着一个试图适用于大多数情况的例子,使用3个对象: Person , Brain和City 。 我们想要克隆一个拥有自己的大脑,但同一个城市的人。 你可以把任何其他方法都可以带来或阅读文章的所有问题。
这是我的结论略有修改的版本:
通过指定
New
跟随类名来复制对象通常会导致不可扩展的代码。 使用克隆,原型模式的应用,是一个更好的方法来实现这一点。 但是,使用C#(和Java)中提供的克隆也是相当成问题的。 最好提供一个受保护的(非公共的)拷贝构造函数,并从clone方法中调用它。 这使我们能够将创build对象的任务委托给类本身的一个实例,从而提供可扩展性,并且使用受保护的拷贝构造函数安全地创build对象。
希望这个实现能够说清楚:
public class Person : ICloneable { 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的想法,我可以分散XML,所以我这样做了:
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这样的第三方应用程序,可以这样做:
MyObject oldObj; // The existing object to clone MyObject newObj = new MyObject(); newObj.InjectFrom(oldObj); // Using ValueInjecter syntax
使用这种方法,您不必在对象上实现ISerializable或ICloneable。 这在MVC / MVVM模式中很常见,所以创build了这样简单的工具。
请参阅CodePlex上的valueinjec深度克隆解决scheme 。
我刚刚创build了CloneExtensions
库项目。 它使用Expression Tree运行时代码编译生成的简单赋值操作执行快速深入的克隆。
如何使用它?
而不是用字段和属性之间的赋值语气编写自己的Clone
或Copy
方法,而是使用expression式树为程序自己做。 标记为扩展方法的GetClone<T>()
方法允许您简单地在您的实例上调用它:
var newInstance = source.GetClone();
您可以使用CloningFlags
枚举来select应该从source
复制到newInstance
CloningFlags
:
var newInstance = source.GetClone(CloningFlags.Properties | CloningFlags.CollectionItems);
什么可以克隆?
- (int,uint,byte,double,char等),已知的不可变types(DateTime,TimeSpan,String)和委托(包括Action,Func等)
- 可空
- T []数组
- 自定义类和结构体,包括generics类和结构体。
类/结构成员在内部克隆:
- 公共的,而不是只读字段的值
- 具有get和set访问器的公共属性的值
- 收集实施ICollectiontypes的项目
它有多快?
解决scheme比reflection更快,因为成员信息只能在给定typesT
第一次使用GetClone<T>
之前收集一次。
当克隆多个相同typesT
实例时,它也比基于序列化的解决scheme更快。
和更多…
阅读有关生成的expression式的更多信息。
List<int>
示例expression式debugging列表:
.Lambda #Lambda1<System.Func`4[System.Collections.Generic.List`1[System.Int32],CloneExtensions.CloningFlags,System.Collections.Generic.IDictionary`2[System.Type,System.Func`2[System.Object,System.Object]],System.Collections.Generic.List`1[System.Int32]]>( System.Collections.Generic.List`1[System.Int32] $source, CloneExtensions.CloningFlags $flags, System.Collections.Generic.IDictionary`2[System.Type,System.Func`2[System.Object,System.Object]] $initializers) { .Block(System.Collections.Generic.List`1[System.Int32] $target) { .If ($source == null) { .Return #Label1 { null } } .Else { .Default(System.Void) }; .If ( .Call $initializers.ContainsKey(.Constant<System.Type>(System.Collections.Generic.List`1[System.Int32])) ) { $target = (System.Collections.Generic.List`1[System.Int32]).Call ($initializers.Item[.Constant<System.Type>(System.Collections.Generic.List`1[System.Int32])] ).Invoke((System.Object)$source) } .Else { $target = .New System.Collections.Generic.List`1[System.Int32]() }; .If ( ((System.Byte)$flags & (System.Byte).Constant<CloneExtensions.CloningFlags>(Fields)) == (System.Byte).Constant<CloneExtensions.CloningFlags>(Fields) ) { .Default(System.Void) } .Else { .Default(System.Void) }; .If ( ((System.Byte)$flags & (System.Byte).Constant<CloneExtensions.CloningFlags>(Properties)) == (System.Byte).Constant<CloneExtensions.CloningFlags>(Properties) ) { .Block() { $target.Capacity = .Call CloneExtensions.CloneFactory.GetClone( $source.Capacity, $flags, $initializers) } } .Else { .Default(System.Void) }; .If ( ((System.Byte)$flags & (System.Byte).Constant<CloneExtensions.CloningFlags>(CollectionItems)) == (System.Byte).Constant<CloneExtensions.CloningFlags>(CollectionItems) ) { .Block( System.Collections.Generic.IEnumerator`1[System.Int32] $var1, System.Collections.Generic.ICollection`1[System.Int32] $var2) { $var1 = (System.Collections.Generic.IEnumerator`1[System.Int32]).Call $source.GetEnumerator(); $var2 = (System.Collections.Generic.ICollection`1[System.Int32])$target; .Loop { .If (.Call $var1.MoveNext() != False) { .Call $var2.Add(.Call CloneExtensions.CloneFactory.GetClone( $var1.Current, $flags, $initializers)) } .Else { .Break #Label2 { } } } .LabelTarget #Label2: } } .Else { .Default(System.Void) }; .Label $target .LabelTarget #Label1: }
}
像下面的c#代码有什么相同的含义:
(source, flags, initializers) => { if(source == null) return null; if(initializers.ContainsKey(typeof(List<int>)) target = (List<int>)initializers[typeof(List<int>)].Invoke((object)source); else target = new List<int>(); if((flags & CloningFlags.Properties) == CloningFlags.Properties) { target.Capacity = target.Capacity.GetClone(flags, initializers); } if((flags & CloningFlags.CollectionItems) == CloningFlags.CollectionItems) { var targetCollection = (ICollection<int>)target; foreach(var item in (ICollection<int>)source) { targetCollection.Add(item.Clone(flags, initializers)); } } return target; }
是不是很像你会如何写你自己的Clone
方法的List<int>
?
简短的答案是你从ICloneable接口inheritance,然后实现.clone函数。 克隆应该进行成员复制,并对需要的成员执行深层复制,然后返回结果对象。 这是一个recursion操作(它要求您要克隆的类的所有成员都是值types或实现ICloneable,并且它们的成员是值types或实现ICloneable等等)。
有关使用ICloneable进行克隆的更多详细说明,请查看本文 。
长的答案是“这取决于”。 正如其他人所提到的那样,ICloneable不被generics所支持,需要对循环类引用进行特殊的考虑,实际上被某些人视为.NET Framework中的“错误” 。 序列化方法取决于你的对象是可序列化的,他们可能不是,你可能无法控制。 社会上还有很多争论是“最好”的做法。 事实上,没有一种解决scheme是所有情况下的一刀切式最佳实践,例如ICloneable最初被解释为是。
有关更多选项,请参阅此开发人员专栏文章 (信任Ian)。
如果你想真正克隆到未知types,你可以看看快速克隆 。
这是基于expression式的克隆工作比二进制序列化快10倍,并保持完整的对象图完整性。
这意味着:如果多次引用层次中的同一个对象,那么这个克隆也会有一个被引用的实例。
不需要接口,属性或对被克隆对象的任何其他修改。
- 基本上你需要实现ICloneable接口,然后实现对象结构的复制。
- 如果是所有成员的深层复制,则需要确保(不涉及您select的解决scheme)所有的孩子都可以克隆。
- 在这个过程中,有时候你需要知道一些限制,例如,如果你复制ORM对象,大部分框架只允许一个对象附加到会话上,而且你不能创build这个对象的克隆,或者如果可能的话,你需要关心关于这些对象的会话附加。
干杯。
最好的是实现一个扩展方法
public static T DeepClone<T>(this T originalObject) { /* the cloning code */ }
然后在解决scheme的任何地方使用它
var copy = anyObject.DeepClone();
我们可以有以下三种实现:
- 通过序列化 (最短的代码)
- 通过reflection – 快5倍
- expression式树 – 速度快20倍
所有链接的方法工作良好,并进行了深入的testing。
我想出了这个来克服.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,但这是不可能的,由于产量不工作匿名方法块内。
更好的是,使用通用的List <T> cloner:
class Utility<T> where T : ICloneable { static public IEnumerable<T> CloneList(List<T> tl) { foreach (T t in tl) { yield return (T)t.Clone(); } } }
这是一个深层复制实现:
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; }
Q. Why would I choose this answer?
- Choose this answer if you want the fastest speed .NET is capable of.
- Ignore this answer if you want a really, really easy method of cloning.
In other words, go with another answer unless you have a performance bottleneck that needs fixing, and you can prove it with a profiler .
10x faster than other methods
The following method of performing a deep clone is:
- 10x faster than anything that involves serialization/deserialization;
- Pretty darn close to the theoretical maximum speed .NET is capable of.
And the method …
For ultimate speed, you can use Nested MemberwiseClone to do a deep copy . Its almost the same speed as copying a value struct, and is much faster than (a) reflection or (b) serialization (as described in other answers on this page).
Note that if you use Nested MemberwiseClone for a deep copy , you have to manually implement a ShallowCopy for each nested level in the class, and a DeepCopy which calls all said ShallowCopy methods to create a complete clone. This is simple: only a few lines in total, see the demo code below.
Here is the output of the code showing the relative performance difference for 100,000 clones:
- 1.08 seconds for Nested MemberwiseClone on nested structs
- 4.77 seconds for Nested MemberwiseClone on nested classes
- 39.93 seconds for Serialization/Deserialization
Using Nested MemberwiseClone on a class almost as fast as copying a struct, and copying a struct is pretty darn close to the theoretical maximum speed .NET is capable of.
Demo 1 of shallow and deep copy, using classes and MemberwiseClone: Create Bob Bob.Age=30, Bob.Purchase.Description=Lamborghini Clone Bob >> BobsSon Adjust BobsSon details BobsSon.Age=2, BobsSon.Purchase.Description=Toy car Proof of deep copy: If BobsSon is a true clone, then adjusting BobsSon details will not affect Bob: Bob.Age=30, Bob.Purchase.Description=Lamborghini Elapsed time: 00:00:04.7795670,30000000 Demo 2 of shallow and deep copy, using structs and value copying: Create Bob Bob.Age=30, Bob.Purchase.Description=Lamborghini Clone Bob >> BobsSon Adjust BobsSon details: BobsSon.Age=2, BobsSon.Purchase.Description=Toy car Proof of deep copy: If BobsSon is a true clone, then adjusting BobsSon details will not affect Bob: Bob.Age=30, Bob.Purchase.Description=Lamborghini Elapsed time: 00:00:01.0875454,30000000 Demo 3 of deep copy, using class and serialize/deserialize: Elapsed time: 00:00:39.9339425,30000000
To understand how to do a deep copy using MemberwiseCopy, here is the demo project that was used to generate the times above:
// Nested MemberwiseClone example. // Added to demo how to deep copy a reference class. [Serializable] // Not required if using MemberwiseClone, only used for speed comparison using serialization. public class Person { public Person(int age, string description) { this.Age = age; this.Purchase.Description = description; } [Serializable] // Not required if using MemberwiseClone public class PurchaseType { public string Description; public PurchaseType ShallowCopy() { return (PurchaseType)this.MemberwiseClone(); } } public PurchaseType Purchase = new PurchaseType(); public int Age; // Add this if using nested MemberwiseClone. // This is a class, which is a reference type, so cloning is more difficult. public Person ShallowCopy() { return (Person)this.MemberwiseClone(); } // Add this if using nested MemberwiseClone. // This is a class, which is a reference type, so cloning is more difficult. public Person DeepCopy() { // Clone the root ... Person other = (Person) this.MemberwiseClone(); // ... then clone the nested class. other.Purchase = this.Purchase.ShallowCopy(); return other; } } // Added to demo how to copy a value struct (this is easy - a deep copy happens by default) public struct PersonStruct { public PersonStruct(int age, string description) { this.Age = age; this.Purchase.Description = description; } public struct PurchaseType { public string Description; } public PurchaseType Purchase; public int Age; // This is a struct, which is a value type, so everything is a clone by default. public PersonStruct ShallowCopy() { return (PersonStruct)this; } // This is a struct, which is a value type, so everything is a clone by default. public PersonStruct DeepCopy() { return (PersonStruct)this; } } // Added only for a speed comparison. public class MyDeepCopy { public static T DeepCopy<T>(T obj) { object result = null; using (var ms = new MemoryStream()) { var formatter = new BinaryFormatter(); formatter.Serialize(ms, obj); ms.Position = 0; result = (T)formatter.Deserialize(ms); ms.Close(); } return (T)result; } }
Then, call the demo from main:
void MyMain(string[] args) { { Console.Write("Demo 1 of shallow and deep copy, using classes and MemberwiseCopy:\n"); var Bob = new Person(30, "Lamborghini"); Console.Write(" Create Bob\n"); Console.Write(" Bob.Age={0}, Bob.Purchase.Description={1}\n", Bob.Age, Bob.Purchase.Description); Console.Write(" Clone Bob >> BobsSon\n"); var BobsSon = Bob.DeepCopy(); Console.Write(" Adjust BobsSon details\n"); BobsSon.Age = 2; BobsSon.Purchase.Description = "Toy car"; Console.Write(" BobsSon.Age={0}, BobsSon.Purchase.Description={1}\n", BobsSon.Age, BobsSon.Purchase.Description); Console.Write(" Proof of deep copy: If BobsSon is a true clone, then adjusting BobsSon details will not affect Bob:\n"); Console.Write(" Bob.Age={0}, Bob.Purchase.Description={1}\n", Bob.Age, Bob.Purchase.Description); Debug.Assert(Bob.Age == 30); Debug.Assert(Bob.Purchase.Description == "Lamborghini"); var sw = new Stopwatch(); sw.Start(); int total = 0; for (int i = 0; i < 100000; i++) { var n = Bob.DeepCopy(); total += n.Age; } Console.Write(" Elapsed time: {0},{1}\n\n", sw.Elapsed, total); } { Console.Write("Demo 2 of shallow and deep copy, using structs:\n"); var Bob = new PersonStruct(30, "Lamborghini"); Console.Write(" Create Bob\n"); Console.Write(" Bob.Age={0}, Bob.Purchase.Description={1}\n", Bob.Age, Bob.Purchase.Description); Console.Write(" Clone Bob >> BobsSon\n"); var BobsSon = Bob.DeepCopy(); Console.Write(" Adjust BobsSon details:\n"); BobsSon.Age = 2; BobsSon.Purchase.Description = "Toy car"; Console.Write(" BobsSon.Age={0}, BobsSon.Purchase.Description={1}\n", BobsSon.Age, BobsSon.Purchase.Description); Console.Write(" Proof of deep copy: If BobsSon is a true clone, then adjusting BobsSon details will not affect Bob:\n"); Console.Write(" Bob.Age={0}, Bob.Purchase.Description={1}\n", Bob.Age, Bob.Purchase.Description); Debug.Assert(Bob.Age == 30); Debug.Assert(Bob.Purchase.Description == "Lamborghini"); var sw = new Stopwatch(); sw.Start(); int total = 0; for (int i = 0; i < 100000; i++) { var n = Bob.DeepCopy(); total += n.Age; } Console.Write(" Elapsed time: {0},{1}\n\n", sw.Elapsed, total); } { Console.Write("Demo 3 of deep copy, using class and serialize/deserialize:\n"); int total = 0; var sw = new Stopwatch(); sw.Start(); var Bob = new Person(30, "Lamborghini"); for (int i = 0; i < 100000; i++) { var BobsSon = MyDeepCopy.DeepCopy<Person>(Bob); total += BobsSon.Age; } Console.Write(" Elapsed time: {0},{1}\n", sw.Elapsed, total); } Console.ReadKey(); }
Again, note that if you use Nested MemberwiseClone for a deep copy , you have to manually implement a ShallowCopy for each nested level in the class, and a DeepCopy which calls all said ShallowCopy methods to create a complete clone. This is simple: only a few lines in total, see the demo code above.
Value types vs. References Types
Note that when it comes to cloning an object, there is is a big difference between a " struct " and a " class ":
- If you have a " struct ", it's a value type so you can just copy it, and the contents will be cloned (but it will only make a shallow clone unless you use the techniques in this post).
- If you have a " class ", it's a reference type , so if you copy it, all you are doing is copying the pointer to it. To create a true clone, you have to be more creative, and use differences between value types and references types which creates another copy of the original object in memory.
See differences between value types and references types .
Checksums to aid in debugging
- Cloning objects incorrectly can lead to very difficult-to-pin-down bugs. In production code, I tend to implement a checksum to double check that the object has been cloned properly, and hasn't been corrupted by another reference to it. This checksum can be switched off in Release mode.
- I find this method quite useful: often, you only want to clone parts of the object, not the entire thing.
Really useful for decoupling many threads from many other threads
One excellent use case for this code is feeding clones of a nested class or struct into a queue, to implement the producer / consumer pattern.
- We can have one (or more) threads modifying a class that they own, then pushing a complete copy of this class into a
ConcurrentQueue
. - We then have one (or more) threads pulling copies of these classes out and dealing with them.
This works extremely well in practice, and allows us to decouple many threads (the producers) from one or more threads (the consumers).
And this method is blindingly fast too: if we use nested structs, it's 35x faster than serializing/deserializing nested classes, and allows us to take advantage of all of the threads available on the machine.
更新
Apparently, ExpressMapper is as fast, if not faster, than hand coding such as above. I might have to see how they compare with a profiler.
Keep things simple and use AutoMapper as others mentioned, it's a simple little library to map one object to another… To copy an object to another with the same type, all you need is three lines of code:
MyType source = new MyType(); Mapper.CreateMap<MyType, MyType>(); MyType target = Mapper.Map<MyType, MyType>(source);
The target object is now a copy of the source object. Not simple enough? Create an extension method to use everywhere in your solution:
public static T Copy<T>(this T source) { T copy = default(T); Mapper.CreateMap<T, T>(); copy = Mapper.Map<T, T>(source); return copy; }
By using the extension method, the three lines become one line:
MyType copy = source.Copy();
In general, you implement the ICloneable interface and implement Clone yourself. C# objects have a built-in MemberwiseClone method that performs a shallow copy that can help you out for all the primitives.
For a deep copy, there is no way it can know how to automatically do it.
I've seen it implemented through reflection as well. Basically there was a method that would iterate through the members of an object and appropriately copy them to the new object. When it reached reference types or collections I think it did a recursive call on itself. Reflection is expensive, but it worked pretty well.
As I couldn't find a cloner that meets all my requirements in different projects, I created a deep cloner that can be configured and adapted to different code structures instead of adapting my code to meet the cloners requirements. Its achieved by adding annotations to the code that shall be cloned or you just leave the code as it is to have the default behaviour. It uses reflection, type caches and is based on fasterflect . The cloning process is very fast for a huge amount of data and a high object hierarchy (compared to other reflection/serialization based algorithms).
https://github.com/kalisohn/CloneBehave
Also available as a nuget package: https://www.nuget.org/packages/Clone.Behave/1.0.0
For example: The following code will deepClone Address, but only perform a shallow copy of the _currentJob field.
public class Person { [DeepClone(DeepCloneBehavior.Shallow)] private Job _currentJob; public string Name { get; set; } public Job CurrentJob { get{ return _currentJob; } set{ _currentJob = value; } } public Person Manager { get; set; } } public class Address { public Person PersonLivingHere { get; set; } } Address adr = new Address(); adr.PersonLivingHere = new Person("John"); adr.PersonLivingHere.BestFriend = new Person("James"); adr.PersonLivingHere.CurrentJob = new Job("Programmer"); Address adrClone = adr.Clone(); //RESULT adr.PersonLivingHere == adrClone.PersonLivingHere //false adr.PersonLivingHere.Manager == adrClone.PersonLivingHere.Manager //false adr.PersonLivingHere.CurrentJob == adrClone.PersonLivingHere.CurrentJob //true adr.PersonLivingHere.CurrentJob.AnyProperty == adrClone.PersonLivingHere.CurrentJob.AnyProperty //true
This method solved the problem for me:
private static MyObj DeepCopy(MyObj source) { var DeserializeSettings = new JsonSerializerSettings { ObjectCreationHandling = ObjectCreationHandling.Replace }; return JsonConvert.DeserializeObject<MyObj >(JsonConvert.SerializeObject(source), DeserializeSettings); }
Use it like this: MyObj a = DeepCopy(b);
I like Copyconstructors like that:
public AnyObject(AnyObject anyObject) { foreach (var property in typeof(AnyObject).GetProperties()) { property.SetValue(this, property.GetValue(anyObject)); } foreach (var field in typeof(AnyObject).GetFields()) { field.SetValue(this, field.GetValue(anyObject)); } }
If you have more things to copy add them
Code Generator
We have seen a lot of ideas from serialization over manual implementation to reflection and I want to propose a totally different approach using the CGbR Code Generator . The generate clone method is memory and CPU efficient and therefor 300x faster as the standard DataContractSerializer.
All you need is a partial class definition with ICloneable
and the generator does the rest:
public partial class Root : ICloneable { public Root(int number) { _number = number; } private int _number; public Partial[] Partials { get; set; } public IList<ulong> Numbers { get; set; } public object Clone() { return Clone(true); } private Root() { } } public partial class Root { public Root Clone(bool deep) { var copy = new Root(); // All value types can be simply copied copy._number = _number; if (deep) { // In a deep clone the references are cloned var tempPartials = new Partial[Partials.Length]; for (var i = 0; i < Partials.Length; i++) { var value = Partials[i]; value = value.Clone(true); tempPartials[i] = value; } copy.Partials = tempPartials; var tempNumbers = new List<ulong>(Numbers.Count); for (var i = 0; i < Numbers.Count; i++) { var value = Numbers[i]; tempNumbers.Add(value); } copy.Numbers = tempNumbers; } else { // In a shallow clone only references are copied copy.Partials = Partials; copy.Numbers = Numbers; } return copy; } }
Note: Latest version has a more null checks, but I left them out for better understanding.
Here a solution fast and easy that worked for me without relaying on Serialization/Deserialization.
public class MyClass { public virtual MyClass DeepClone() { var returnObj = (MyClass)MemberwiseClone(); var type = returnObj.GetType(); var fieldInfoArray = type.GetRuntimeFields().ToArray(); foreach (var fieldInfo in fieldInfoArray) { object sourceFieldValue = fieldInfo.GetValue(this); if (!(sourceFieldValue is MyClass)) { continue; } var sourceObj = (MyClass)sourceFieldValue; var clonedObj = sourceObj.DeepClone(); fieldInfo.SetValue(returnObj, clonedObj); } return returnObj; } }
EDIT : requires
using System.Linq; using System.Reflection;
That's How I used it
public MyClass Clone(MyClass theObjectIneededToClone) { MyClass clonedObj = theObjectIneededToClone.DeepClone(); }
I think you can try this.
MyObject myObj = GetMyObj(); // Create and fill a new object MyObject newObj = new MyObject(myObj); //DeepClone it
按着这些次序:
- Define an
ISelf<T>
with a read-onlySelf
property that returnsT
, andICloneable<out T>
, which derives fromISelf<T>
and includes a methodT Clone()
. - Then define a
CloneBase
type which implements aprotected virtual generic VirtualClone
castingMemberwiseClone
to the passed-in type. - Each derived type should implement
VirtualClone
by calling the base clone method and then doing whatever needs to be done to properly clone those aspects of the derived type which the parent VirtualClone method hasn't yet handled.
For maximum inheritance versatility, classes exposing public cloning functionality should be sealed
, but derive from a base class which is otherwise identical except for the lack of cloning. Rather than passing variables of the explicit clonable type, take a parameter of type ICloneable<theNonCloneableType>
. This will allow a routine that expects a cloneable derivative of Foo
to work with a cloneable derivative of DerivedFoo
, but also allow the creation of non-cloneable derivatives of Foo
.
I have created a version of the accepted answer that works with both '[Serializable]' and '[DataContract]'. It has been a while since I wrote it, but if I remember correctly [DataContract] needed a different serializer.
Requires System, System.IO, System.Runtime.Serialization, System.Runtime.Serialization.Formatters.Binary, System.Xml ;
public static class ObjectCopier { /// <summary> /// Perform a deep Copy of an object that is marked with '[Serializable]' or '[DataContract]' /// </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 == true) { return CloneUsingSerializable<T>(source); } if (IsDataContract(typeof(T)) == true) { return CloneUsingDataContracts<T>(source); } throw new ArgumentException("The type must be Serializable or use DataContracts.", "source"); } /// <summary> /// Perform a deep Copy of an object that is marked with '[Serializable]' /// </summary> /// <remarks> /// Found on http://stackoverflow.com/questions/78536/cloning-objects-in-c-sharp /// Uses code found on CodeProject, which allows free use in third party apps /// - http://www.codeproject.com/KB/tips/SerializedObjectCloner.aspx /// </remarks> /// <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 CloneUsingSerializable<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); } } /// <summary> /// Perform a deep Copy of an object that is marked with '[DataContract]' /// </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 CloneUsingDataContracts<T>(T source) { if (IsDataContract(typeof(T)) == false) { throw new ArgumentException("The type must be a data contract.", "source"); } // ** Don't serialize a null object, simply return the default for that object if (Object.ReferenceEquals(source, null)) { return default(T); } DataContractSerializer dcs = new DataContractSerializer(typeof(T)); using(Stream stream = new MemoryStream()) { using (XmlDictionaryWriter writer = XmlDictionaryWriter.CreateBinaryWriter(stream)) { dcs.WriteObject(writer, source); writer.Flush(); stream.Seek(0, SeekOrigin.Begin); using (XmlDictionaryReader reader = XmlDictionaryReader.CreateBinaryReader(stream, XmlDictionaryReaderQuotas.Max)) { return (T)dcs.ReadObject(reader); } } } } /// <summary> /// Helper function to check if a class is a [DataContract] /// </summary> /// <param name="type">The type of the object to check.</param> /// <returns>Boolean flag indicating if the class is a DataContract (true) or not (false) </returns> public static bool IsDataContract(Type type) { object[] attributes = type.GetCustomAttributes(typeof(DataContractAttribute), false); return attributes.Length == 1; } }
To clone your class object you can use the Object.MemberwiseClone method,
just add this function to your class :
public class yourClass { // ... // ... public yourClass DeepCopy() { yourClass othercopy = (yourClass)this.MemberwiseClone(); return othercopy; } }
then to perform a deep independant copy, just call the DeepCopy method :
yourClass newLine = oldLine.DeepCopy();
希望这可以帮助。
Ok, there are some obvious example with reflection in this post, BUT reflection is usually slow, until you start to cache it properly.
if you'll cache it properly, than it'll deep clone 1000000 object by 4,6s (measured by Watcher).
static readonly Dictionary<Type, PropertyInfo[]> ProperyList = new Dictionary<Type, PropertyInfo[]>();
than you take cached properties or add new to dictionary and use them simply
foreach (var prop in propList) { var value = prop.GetValue(source, null); prop.SetValue(copyInstance, value, null); }
full code check in my post in another answer
If your Object Tree is Serializeable you could also use something like this
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; }
be informed that this Solution is pretty easy but it's not as performant as other solutions may be.
And be sure that if the Class grows, there will still be only those fields cloned, which also get serialized.