我想做的事情如下:
MyObject myObj = GetMyObj(); // Create and fill a new object MyObject newObj = myObj.Clone();
然后更改未在原始对象中反映的新对象.
我不经常需要这个功能,所以当有必要的时候,我已经使用了创建一个新对象然后单独复制每个属性,但它总是让我觉得有更好或更优雅的处理方式情况.
如何克隆或深度复制对象,以便可以修改克隆对象而不会在原始对象中反映任何更改?
虽然标准的做法是实现ICloneable
界面(这里描述,所以我不会反刍),这是我在The Code Project上发现的一个很好的深度克隆对象复制器,并将其合并到我们的东西中.
如其他地方所述,它确实需要您的对象可序列化.
using System; using System.IO; using System.Runtime.Serialization; using System.Runtime.Serialization.Formatters.Binary; ////// 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. /// public static class ObjectCopier { ////// Perform a deep Copy of the object. /// ///The type of object being copied. /// The object instance to copy. ///The copied object. public static T Clone(T source) { if (!typeof(T).IsSerializable) { throw new ArgumentException("The type must be serializable.", nameof(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(this T source) { //... }
现在方法调用就变成了objectBeingCloned.Clone();
.
编辑(2015年1月10日)以为我会重新考虑这一点,提到我最近开始使用(Newtonsoft)Json这样做,它应该更轻,并避免[Serializable]标签的开销.(注意 @atconway在评论中指出私有成员不使用JSON方法克隆)
////// Perform a deep Copy of the object, using Json as a serialisation method. NOTE: Private members are not cloned using this method. /// ///The type of object being copied. /// The object instance to copy. ///The copied object. public static T CloneJson(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 (JsonConvert.SerializeObject(source), deserializeSettings); }
我想要一个非常简单的对象,主要是原始和列表的克隆人.如果你的对象是开箱即用的JSON serializable,那么这个方法就可以了.这不需要修改或实现克隆类上的接口,只需要像JSON.NET这样的JSON序列化程序.
public static T Clone(T source) { var serialized = JsonConvert.SerializeObject(source); return JsonConvert.DeserializeObject (serialized); }
此外,您可以使用此扩展方法
public static class SystemExtension { public static T Clone(this T source) { var serialized = JsonConvert.SerializeObject(source); return JsonConvert.DeserializeObject (serialized); } }
不使用的原因ICloneable是不是因为它没有一个通用的接口. 不使用它的原因是因为它含糊不清.它不清楚你是否得到浅或副本; 这取决于实施者.
是的,MemberwiseClone
做一个浅的副本,但相反的MemberwiseClone
是不是Clone
; 或许DeepClone
,它可能不存在.当您通过其ICloneable接口使用对象时,您无法知道底层对象执行哪种克隆.(并且XML注释不会说清楚,因为您将获得接口注释而不是对象的Clone方法上的注释.)
我通常做的只是制作一个Copy
完全符合我想要的方法.
在详细阅读了这里链接的许多选项以及此问题的可能解决方案之后,我相信所有选项都在Ian P的链接中得到了很好的总结(所有其他选项都是这些选项的变体),最佳解决方案由Pedro77关于问题评论的链接.
所以我将在这里复制这两个参考文献的相关部分.这样我们可以:
首先,这些都是我们的选择:
手动使用ICloneable,它是浅层的,不是类型安全的
MemberwiseClone,使用ICloneable
使用 Activator.CreateInstance和递归的MemberwiseClone进行反射
序列化,正如 johnc的首选答案所指出的那样
中级语言,我不知道它是如何工作的
扩展方法,例如Havard Straden的这个自定义克隆框架
表达树
" 表达式树快速复制 "一文 还对序列化,反射和表达式树的克隆进行了性能比较.
为什么我选择ICloneable(即手动)Venkat Subramaniam先生(此处的冗余链接)详细解释了原因.
他的所有文章围绕着一个试图适用于大多数情况的例子,使用3个对象:人,大脑和城市.我们想要克隆一个人,它将拥有自己的大脑但是同一个城市.你可以想象上面的任何其他方法可以带来或阅读文章的所有问题.
这是他对他的结论的略微修改版本:
通过指定
New
后跟类名来复制对象通常会导致代码不可扩展.使用clone,原型模式的应用,是实现这一目标的更好方法.但是,使用C#(和Java)中提供的克隆也很成问题.最好提供受保护(非公共)的复制构造函数,并从克隆方法中调用它.这使我们能够将创建对象的任务委托给类本身的实例,从而提供可扩展性,并使用受保护的拷贝构造函数安全地创建对象.
希望这个实现可以使事情变得清晰:
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(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 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 objClone) where T:new() { string GetString = SerializeHelper.SeralizeObjectToXML (objClone); return SerializeHelper.DeserializeXML (GetString); } }
我刚刚创建了CloneExtensions
库项目.它使用Expression Tree运行时代码编译生成的简单赋值操作执行快速,深度克隆.
如何使用它?
使用表达式树,程序不是使用字段和属性之间的分配语音编写自己的方法Clone
或Copy
方法.GetClone
标记为扩展方法的方法允许您在实例上简单地调用它:
var newInstance = source.GetClone();
您可以选择什么应该被复制source
到newInstance
使用CloningFlags
枚举:
var newInstance = source.GetClone(CloningFlags.Properties | CloningFlags.CollectionItems);
什么可以克隆?
Primitive(int,uint,byte,double,char等),已知的不可变类型(DateTime,TimeSpan,String)和委托(包括Action,Func等)
可空
T []数组
自定义类和结构,包括泛型类和结构.
类/ struct成员在内部克隆:
公共值,而不是只读字段
具有get和set访问器的公共属性的值
实现ICollection的类型的集合项
它有多快?
解决方案比反射更快,因为成员信息只需要收集一次,之后GetClone
第一次用于给定类型T
.
当您克隆更多然后耦合相同类型的实例时,它也比基于序列化的解决方案更快T
.
和更多...
了解更多关于所产生表达式的文档.
示例表达式调试列表List
:
.Lambda #Lambda1( 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.Collections.Generic.List`1[System.Int32])) ) { $target = (System.Collections.Generic.List`1[System.Int32]).Call ($initializers.Item[.Constant (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 (Fields)) == (System.Byte).Constant (Fields) ) { .Default(System.Void) } .Else { .Default(System.Void) }; .If ( ((System.Byte)$flags & (System.Byte).Constant (Properties)) == (System.Byte).Constant (Properties) ) { .Block() { $target.Capacity = .Call CloneExtensions.CloneFactory.GetClone( $source.Capacity, $flags, $initializers) } } .Else { .Default(System.Void) }; .If ( ((System.Byte)$flags & (System.Byte).Constant (CollectionItems)) == (System.Byte).Constant (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)) target = (List )initializers[typeof(List )].Invoke((object)source); else target = new List (); if((flags & CloningFlags.Properties) == CloningFlags.Properties) { target.Capacity = target.Capacity.GetClone(flags, initializers); } if((flags & CloningFlags.CollectionItems) == CloningFlags.CollectionItems) { var targetCollection = (ICollection )target; foreach(var item in (ICollection )source) { targetCollection.Add(item.Clone(flags, initializers)); } } return target; }
这不是很像你如何编写自己的Clone
方法List
吗?
如果您已经使用像一个第三方应用程序ValueInjecter或Automapper,你可以这样做:
MyObject oldObj; // The existing object to clone MyObject newObj = new MyObject(); newObj.InjectFrom(oldObj); // Using ValueInjecter syntax
使用此方法,您不必在对象上实现ISerializable或ICloneable.这在MVC/MVVM模式中很常见,因此创建了这样的简单工具.
在CodePlex上看到值得深入克隆的解决方案.
简短的回答是您从ICloneable接口继承然后实现.clone函数.克隆应该执行成员复制并对需要它的任何成员执行深层复制,然后返回结果对象.这是一个递归操作(它要求您要克隆的类的所有成员都是值类型或实现ICloneable,并且它们的成员是值类型或实现ICloneable,依此类推).
有关使用ICloneable克隆的更详细说明,请查看本文.
该长的答案是"看情况".正如其他人所提到的,ICloneable不受泛型支持,需要对循环类引用进行特殊考虑,并且实际上被某些人视为.NET Framework中的"错误".序列化方法取决于您的对象是可序列化的,它们可能不是,您可能无法控制.社区中仍然存在很多争论,即"最佳"做法.实际上,对于像ICloneable最初被解释为的所有情况,所有解决方案都不是一刀切.
有关更多选项,请参阅此Developer's Corner文章(归功于Ian).
最好的方法是实现类似的扩展方法
public static T DeepClone(this T originalObject) { /* the cloning code */ }
然后在解决方案的任何地方使用它
var copy = anyObject.DeepClone();
我们可以有以下三种实现:
通过序列化(最短的代码)
反思 -快5倍
通过表达树 -快20倍
所有链接的方法都很好,并经过深入测试.
基本上你需要实现ICloneable接口然后实现对象结构复制.
如果它是所有成员的深层副本,您需要保证(不与您选择的解决方案相关)所有儿童都可以克隆.
有时您需要在此过程中注意一些限制,例如,如果您复制ORM对象,大多数框架只允许一个对象附加到会话,并且您不能创建此对象的克隆,或者您可能需要关心关于这些对象的会话附加.
干杯.
如果你想要真正的克隆到未知类型,你可以看看 fastclone.
这是基于表达式的克隆,比二进制序列化快10倍,并保持完整的对象图完整性.
这意味着:如果您多次引用层次结构中的同一个对象,则克隆也会引用一个实例.
不需要对正在克隆的对象进行接口,属性或任何其他修改.
保持简单并像其他人一样使用AutoMapper,它是一个简单的小库,可以将一个对象映射到另一个...要将对象复制到另一个具有相同类型的对象,您只需要三行代码:
MyType source = new MyType(); Mapper.CreateMap(); MyType target = Mapper.Map (source);
目标对象现在是源对象的副本.不够简单?创建一个扩展方法,以便在解决方案中的任
public static T Copy(this T source) { T copy = default(T); Mapper.CreateMap (); copy = Mapper.Map (source); return copy; }
通过使用扩展方法,三行成为一行:
MyType copy = source.Copy();
我提出这个来克服.NET必须手动深度复制List
我用这个:
static public IEnumerableCloneList(List 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
class Utilitywhere T : ICloneable { static public IEnumerable CloneList(List tl) { foreach (T t in tl) { yield return (T)t.Clone(); } } }
如果您想要.NET能够以最快的速度选择此答案.
如果你想要一种非常简单的克隆方法,请忽略这个答案.
换句话说,除非您有需要修复的性能瓶颈,否则请使用其他答案,并且您可以使用分析器来证明它.
以下执行深度克隆的方法是:
比涉及序列化/反序列化的任何内容快10倍;
非常接近.NET能够达到的理论最大速度.
要获得最高速度,可以使用嵌套的MemberwiseClone进行深层复制.它与复制值结构的速度几乎相同,并且比(a)反射或(b)序列化快得多(如本页其他答案中所述).
请注意,如果您使用嵌套的MemberwiseClone进行深层复制,则必须为类中的每个嵌套级别手动实现ShallowCopy,并使用DeepCopy调用所有所述的ShallowCopy方法来创建完整的克隆.这很简单:总共只有几行,请参阅下面的演示代码.
以下是代码的输出,显示100,000个克隆的相对性能差异:
嵌套结构上嵌套的MemberwiseClone为1.08秒
嵌套类的嵌套MemberwiseClone为4.77秒
序列化/反序列化为39.93秒
在类上使用嵌套的MemberwiseClone几乎与复制结构一样快,并且复制结构非常接近.NET能够达到的理论最大速度.
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
要了解如何使用MemberwiseCopy执行深层复制,以下是用于生成上述时间的演示项目:
// 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 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; } }
然后,从main调用demo:
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(Bob); total += BobsSon.Age; } Console.Write(" Elapsed time: {0},{1}\n", sw.Elapsed, total); } Console.ReadKey(); }
同样,请注意,如果您使用嵌套的MemberwiseClone进行深层复制,则必须为类中的每个嵌套级别手动实现ShallowCopy,并使用DeepCopy调用所有所述的ShallowCopy方法来创建完整的克隆.这很简单:总共只有几行,请参阅上面的演示代码.
请注意,在克隆对象时," struct "和" class " 之间存在很大差异:
如果你有一个" struct ",它就是一个值类型,所以你可以复制它,然后克隆内容(但除非你使用这篇文章中的技术,否则它只会做一个浅层克隆).
如果你有一个" 类 ",它是一个引用类型,所以如果你复制它,你所做的只是将指针复制到它.要创建真正的克隆,您必须更具创造性,并使用值类型和引用类型之间的差异,这会在内存中创建原始对象的另一个副本.
查看值类型和引用类型之间的差异.
错误地克隆对象可能导致非常难以确定的错误.在生产代码中,我倾向于实现校验和以仔细检查对象是否已正确克隆,并且没有被另一个对它的引用破坏.可以在释放模式下关闭此校验和.
我发现这个方法非常有用:通常,你只想克隆对象的一部分,而不是整个事物.
此代码的一个优秀用例是将嵌套类或结构的克隆提供到队列中,以实现生产者/消费者模式.
我们可以有一个(或多个)线程修改他们拥有的类,然后将该类的完整副本推送到ConcurrentQueue
.
然后我们有一个(或多个)线程将这些类的副本拉出来并处理它们.
这在实践中非常有效,并且允许我们将许多线程(生成器)与一个或多个线程(消费者)分离.
而且这种方法也非常快:如果我们使用嵌套结构,它比串行化/反序列化嵌套类快35倍,并允许我们利用机器上可用的所有线程.
更新显然,ExpressMapper与上面的手动编码一样快,如果不是更快的话.我可能要看看它们与分析器的比较.
通常,您实现ICloneable接口并自己实现Clone.C#对象具有内置的MemberwiseClone方法,该方法执行浅拷贝,可以帮助您处理所有原语.
对于深层复制,它无法知道如何自动执行.
我已经看到它也通过反射实现.基本上有一种方法可以遍历对象的成员并适当地将它们复制到新对象.当它到达引用类型或集合时,我认为它对它自己进行了递归调用.反思很昂贵,但效果很好.
这是一个深层复制实现:
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; }
由于我找不到满足我在不同项目中的所有要求的克隆人,我创建了一个深度克隆者,可以配置和适应不同的代码结构,而不是调整我的代码以满足克隆者的要求.它是通过在应该克隆的代码中添加注释来实现的,或者只是保留代码,因为它具有默认行为.它使用反射,类型缓存并基于更快的反射.对于大量数据和高对象层次结构(与其他基于反射/序列化的算法相比),克隆过程非常快.
https://github.com/kalisohn/CloneBehave
也可作为nuget包提供:https://www.nuget.org/packages/Clone.Behave/1.0.0
例如:以下代码将为deepClone Address,但仅执行_currentJob字段的浅表副本.
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
这个方法为我解决了这个问题:
private static MyObj DeepCopy(MyObj source) { var DeserializeSettings = new JsonSerializerSettings { ObjectCreationHandling = ObjectCreationHandling.Replace }; return JsonConvert.DeserializeObject(JsonConvert.SerializeObject(source), DeserializeSettings); }
像这样使用它: MyObj a = DeepCopy(b);
我喜欢像这样的Copyconstructors:
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)); } }
如果您有更多要复制的东西,请添加它们
我们已经看到了从手动实现到反射的序列化的很多想法,我想使用CGbR代码生成器提出一种完全不同的方法.生成克隆方法是内存和CPU效率,因此比标准DataContractSerializer快300倍.
你需要的只是一个部分类定义,ICloneable
而生成器完成剩下的工作:
public partial class Root : ICloneable { public Root(int number) { _number = number; } private int _number; public Partial[] Partials { get; set; } public IListNumbers { 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 (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; } }
注意:最新版本有一个更多的空检查,但我把它们留下来以便更好地理解.
这里的解决方案快速而简单,对我有用而无需继续序列化/反序列化.
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; } }
编辑:要求
using System.Linq; using System.Reflection;
这就是我如何使用它
public MyClass Clone(MyClass theObjectIneededToClone) { MyClass clonedObj = theObjectIneededToClone.DeepClone(); }
我想你可以试试看。
MyObject myObj = GetMyObj(); // Create and fill a new object MyObject newObj = new MyObject(myObj); //DeepClone it
跟着这些步骤:
定义一个ISelf
具有只读Self
属性的,该属性返回T
和ICloneable
,该属性派生自ISelf
并包含方法T Clone()
。
然后定义一个CloneBase
类型,该类型实现对传入类型的protected virtual generic VirtualClone
强制转换MemberwiseClone
。
每个派生类型都应VirtualClone
通过调用基本clone方法来实现,然后执行所需的任何操作以正确克隆父VirtualClone方法尚未处理的派生类型的那些方面。
为了最大程度地继承通用性,公开公共克隆功能的类应为sealed
,但派生自基类,除非缺少克隆,否则基类是相同的。而不是传递显式可克隆类型的变量,而应使用type的参数ICloneable
。这将允许期望的可克隆派生Foo
与的可克隆派生一起使用的例程 DerivedFoo
,但也允许创建的不可克隆派生Foo
。