我想要做的事,如:
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();
然后更改未反映在原始对象的新对象。
我不经常需要此功能,所以当它是必要的,我已经使出创建一个新的对象,然后逐个复制每个属性,但它总是给我留下了有处理的更好或更优雅的方式感觉的情况。
我如何克隆或深复制一个对象,以使克隆的对象可以不反映在原始对象的任何变化进行修改?
虽然标准的做法是实现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.", 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<T>(this T source)
{
//...
}
现在该方法调用简单地变得objectBeingCloned.Clone();
。
编辑 (2015年1月10日)想我会重新考虑这一点,更何况我最近使用(Newtonsoft)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序列像JSON.NET。
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的评论将不说清楚,因为你会得到接口的意见,而不是对象的克隆方法的人。)
我最常做的是简单地做一个Copy
的方法是不正是我想要的东西。
After much much reading about many of the options linked here, and possible solutions for this issue, I believe all the options are summarized pretty well at Ian P's link (all other options are variations of those) and the best solution is provided by Pedro77's link on the question comments.
So I'll just copy relevant parts of those 2 references here. That way we can have:
First and foremost, those are all our options:
The article Fast Deep Copy by Expression Trees has also performance comparison of cloning by Serialization, Reflection and Expression Trees.
Mr Venkat Subramaniam (redundant link here) explains in much detail why.
All his article circles around an example that tries to be applicable for most cases, using 3 objects: Person, Brain and City. We want to clone a person, which will have its own brain but the same city. You can either picture all problems any of the other methods above can bring or read the article.
This is my slightly modified version of his conclusion:
Copying an object by specifying
New
followed by the class name often leads to code that is not extensible. Using clone, the application of prototype pattern, is a better way to achieve this. However, using clone as it is provided in C# (and Java) can be quite problematic as well. It is better to provide a protected (non-public) copy constructor and invoke that from the clone method. This gives us the ability to delegate the task of creating an object to an instance of a class itself, thus providing extensibility and also, safely creating the objects using the protected copy constructor.
Hopefully this implementation can make things clear:
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);
}
…
}
Now consider having a class derive from 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();
}
}
You may try running the following code:
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);
}
}
The output produced will be:
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
Observe that, if we keep a count of the number of objects, the clone as implemented here will keep a correct count of the number of objects.
我喜欢拷贝构造函数的克隆。 这样做的目的是更清晰。
简单的扩展方法来复制所有的公共属性。 适用于任何物体,并且不需要类是[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 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);
}
}
我刚刚创建CloneExtensions
库项目。 它使用表达式树运行时代码编译生成简单的赋值操作执行快速,深克隆。
如何使用它?
而不是写你自己的Clone
或Copy
与字段和属性之间分配的音方法使程序做自己,使用表达式树。 GetClone<T>()
标记方法,扩展方法可以让你简单地调用它在您的实例:
var newInstance = source.GetClone();
您可以选择什么应该被复制source
到newInstance
使用CloningFlags
枚举:
var newInstance
= source.GetClone(CloningFlags.Properties | CloningFlags.CollectionItems);
什么可以被克隆?
下面的类/结构成员在内部克隆:
它有多快?
解决的办法是更快然后反射,因为成员的信息必须被一次收集,前GetClone<T>
用于第一次对于给定类型T
。
它也比基于系列化的解决方案更快,当你克隆更多的则相同类型的情侣实例T
。
和更多...
了解更多关于所产生表达式的文档 。
对于样品表达调试列表List<int>
:
.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>
?
如果您已经使用像一个第三方应用程序ValueInjecter或Automapper ,你可以这样做:
MyObject oldObj; // The existing object to clone
MyObject newObj = new MyObject();
newObj.InjectFrom(oldObj); // Using ValueInjecter syntax
使用这种方法,你不必实现了ISerializable或ICloneable你的对象。 这是常见的与MVC / MVVM模式,已建立这样的那么简单工具。
见CodePlex上的valueinjecter深克隆解决方案 。
最好是实现一个扩展方法一样
public static T DeepClone<T>(this T originalObject)
{ /* the cloning code */ }
然后通过该解决方案的任何地方使用它
var copy = anyObject.DeepClone();
我们可以有以下三种实现方式:
所有链接的方法是很好的工作,并进行了深入的检验。
简短的回答是,你从ICloneable接口继承,然后实现.clone功能。 克隆应该做一个成员复制和需要它的任何成员执行深拷贝,然后返回生成的对象。 这是一个递归操作(它要求你要克隆的类的所有成员要么值类型或实现ICloneable,他们的成员要么值类型或实施ICloneable,等等)。
对于使用ICloneable克隆上一个更详细的解释,看看这篇文章 。
长的答案是“看情况”。 正如其他人所说,ICloneable不支持泛型,需要对循环类引用特殊的考虑,而实际上是由一些作为观看“错误”在.NET Framework。 序列化方法取决于你的对象是序列化的,他们可能没有,你可能没有控制权。 还有在社会上很多争论这是“最好”的做法。 在现实中,没有一个解决方案是一个一刀切的最佳实践像ICloneable所有情况下最初的解释是。
看到这个开发者园地文章的几个选项(信贷伊恩)。
Cheers.
编辑:项目终止
如果你想真正的克隆到未知类型的,你可以看看fastclone 。
这是基于表达克隆的工作比二进制序列化快约10倍,维持完整的对象图的完整性。
这意味着:如果您在层次结构是指多次同一个对象,克隆也将有一个实例beeing引用。
有没有必要的接口,属性或任何其他修改的对象被克隆。
让事情变得简单,并使用AutoMapper如其他人所说,这是一个简单的小库,一个对象映射到另一个...要将对象复制到另一个同类型的,你需要的是三行代码:
MyType source = new MyType();
Mapper.CreateMap<MyType, MyType>();
MyType target = Mapper.Map<MyType, MyType>(source);
现在的目标对象是所述源对象的一个副本。 不是很简单? 创建于您的解决方案在任何地方使用扩展方法:
public static T Copy<T>(this T source)
{
T copy = default(T);
Mapper.CreateMap<T, T>();
copy = Mapper.Map<T, T>(source);
return copy;
}
通过使用扩展的方法,所述三线成为一条线:
MyType copy = source.Copy();
我想出了这个克服.NET不必手动深拷贝名单<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做这个工作,但它是不可能的,因为产生匿名方法块内部不能正常工作。
更妙的是,使用通用名单<T>拷贝机:
class Utility<T> where T : ICloneable
{
static public IEnumerable<T> CloneList(List<T> tl)
{
foreach (T t in tl)
{
yield return (T)t.Clone();
}
}
}
换句话说, 去另外一个答案,除非你有一个需要固定的性能瓶颈,您可以用探查证明这一点 。
进行深克隆的以下方法是:
对于最终的速度,你可以使用嵌套MemberwiseClone做了深刻的副本 。 其几乎相同的速度复制值结构,并且比(a)中反射或(b)的序列化(因为这页上的在其他的答案描述)要快得多。
需要注意的是,如果你使用嵌套MemberwiseClone一个深拷贝 ,你必须手动实现对类中的每个嵌套级别ShallowCopy和deepcopy的哪些呼叫都表示ShallowCopy方法来创建一个完整的克隆。 这是简单的:只有在总的几行,见下面的演示代码。
这里是表示用于克隆100000的相对性能差的代码的输出:
一类使用嵌套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>(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;
}
}
然后,请从主要演示:
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();
}
再次,注意, 如果您使用嵌套MemberwiseClone一个深拷贝 ,你必须手动实现对类中的每个嵌套级别ShallowCopy和deepcopy的其中要求所有说ShallowCopy方法来创建一个完整的克隆。 这是简单的:只有在总的几行,看到上面的演示代码。
需要注意的是,当涉及到克隆的目的,是“ 结构 ”和“ 类 ”之间有很大的区别:
请参见值类型和引用类型之间的差异 。
一个优良的使用案例该代码供给嵌套类或结构的克隆放入一个队列,以实现生产者/消费者图案。
ConcurrentQueue
。 这个作品非常好于实践,使我们能够从一个或多个线程(消费者)分离的多个线程(生产者)。
这种方法是令人眼花缭乱的快速过:如果我们使用嵌套结构,它比串行/解串嵌套类快35倍,并允许我们把所有的机器上可用的线程的优势。
显然,ExpressMapper一样快,如果不是更快,比手工如上编码等。 我可能要看看他们如何与分析器进行比较。
在一般情况下,要实现ICloneable接口和实施克隆自己。 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;
}
因为我无法找到满足我的不同项目中的所有要求克隆者,我创建了一个深拷贝机可以被配置成适合于不同的代码结构,而不是适应我的代码,以满足仿制的要求。 它通过添加注释到应该克隆,或者你只是离开的代码,因为它是有默认行为的代码来实现的。 它采用反射,类型缓存和基于fasterflect 。 在克隆过程是一个巨大的数据量和高的对象分层结构(相对于其他反射/串行化的基于算法)非常快。
https://github.com/kalisohn/CloneBehave
也可作为NuGet包: https://www.nuget.org/packages/Clone.Behave/1.0.0
例如:下面的代码将deepClone地址,但仅执行_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<MyObj >(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高效和为此300X更快作为标准的DataContractSerializer。
所有你需要的是一个局部的类定义与ICloneable
和发电机没有休息:
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;
}
}
注:最新版本有更多的空检查,但我离开他们出了更好的理解。
这里的解决方案快速,这对我的工作没有中继的序列化/反序列化容易。
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<T>
与只读Self
返回属性T
,和ICloneable<out T>
它从派生ISelf<T>
并且包括方法T Clone()
CloneBase
它实现了一种protected virtual generic VirtualClone
铸造MemberwiseClone
到传入的类型。 VirtualClone
通过调用基克隆方法,然后做一切需要做的正确克隆的母VirtualClone方法尚未处理的派生类型的那些方面。 为了获得最大的多功能性的继承,类暴露公共克隆功能应该被sealed
,但是从基类,它是除了缺乏克隆的其它方面相同的派生。 而不是传递明确可克隆类型的变量,取类型的参数ICloneable<theNonCloneableType>
这将允许预期的复制,则衍生物的常规Foo
用的可复制的,衍生的工作DerivedFoo
,也让非可复制的衍生品的创作Foo
。
我创建了一个版本接受的答案是既“[Serializable接口]”和“[DataContract]”作品。 它已经有一段时间,因为我写的,但如果我没有记错[DataContract]需要一个不同的序列化。
需要系统,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;
}
}
要克隆你的类对象可以使用Object.MemberwiseClone方法,
只需添加此功能类:
public class yourClass
{
// ...
// ...
public yourClass DeepCopy()
{
yourClass othercopy = (yourClass)this.MemberwiseClone();
return othercopy;
}
}
然后进行深独立的副本,只需调用deepcopy的方法:
yourClass newLine = oldLine.DeepCopy();
希望这可以帮助。
好了,还有在这篇文章反映了一些明显的例子,但反射通常是缓慢的,直到你开始正确地进行缓存。
如果你正确地缓存它,比它会通过4,6s深克隆百万对象(通过观察测量)。
static readonly Dictionary<Type, PropertyInfo[]> ProperyList = new Dictionary<Type, PropertyInfo[]>();
比你需要缓存的属性或添加新字典和简单地使用它们
foreach (var prop in propList)
{
var value = prop.GetValue(source, null);
prop.SetValue(copyInstance, value, null);
}
完整的代码检查在我的岗位在另一个答案
https://stackoverflow.com/a/34365709/4711853
如果你的对象树是可序列化,你也可以使用这样的事情
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;
}
被告知,此解决方案是很容易的,但它并不像高性能的其他解决方案可能。
而且可以肯定的是,如果阶层的增长,仍然会有只有那些领域克隆,这也得到初始化。