C# 深度克隆对象
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/78536/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Deep cloning objects
提问by NakedBrunch
I want to do something like:
我想做类似的事情:
MyObject myObj = GetMyObj(); // Create and fill a new object
MyObject newObj = myObj.Clone();
And then make changes to the new object that are not reflected in the original object.
然后对未反映在原始对象中的新对象进行更改。
I don't often need this functionality, so when it's been necessary, I've resorted to creating a new object and then copying each property individually, but it always leaves me with the feeling that there is a better or more elegant way of handling the situation.
我并不经常需要这个功能,所以当有必要时,我会诉诸于创建一个新对象,然后单独复制每个属性,但它总是让我觉得有一种更好或更优雅的处理方式情况。
How can I clone or deep copy an object so that the cloned object can be modified without any changes being reflected in the original object?
如何克隆或深度复制对象,以便可以修改克隆的对象而不会在原始对象中反映任何更改?
采纳答案by johnc
Whilst the standard practice is to implement the ICloneable
interface (described here, so I won't regurgitate), here's a nice deep clone object copier I found on The Code Projecta while ago and incorporated it in our stuff.
虽然标准的做法是实现ICloneable
接口(这里描述,所以我不会反驳),但这是我不久前在The Code Project上找到的一个很好的深度克隆对象复制器,并将它合并到我们的东西中。
As mentioned elsewhere, it does require your objects to be serializable.
正如其他地方所提到的,它确实要求您的对象是可序列化的。
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);
}
}
}
The idea is that it serializes your object and then deserializes it into a fresh object. The benefit is that you don't have to concern yourself about cloning everything when an object gets too complex.
这个想法是它序列化您的对象,然后将其反序列化为一个新对象。好处是当对象变得太复杂时,您不必担心克隆所有内容。
And with the use of extension methods (also from the originally referenced source):
并使用扩展方法(也来自最初引用的来源):
In case you prefer to use the new extension methodsof C# 3.0, change the method to have the following signature:
如果您更喜欢使用C# 3.0的新扩展方法,请将方法更改为具有以下签名:
public static T Clone<T>(this T source)
{
//...
}
Now the method call simply becomes objectBeingCloned.Clone();
.
现在方法调用简单地变成了objectBeingCloned.Clone();
.
EDIT(January 10 2015) Thought I'd revisit this, to mention I recently started using (Newtonsoft) Json to do this, it should belighter, and avoids the overhead of [Serializable] tags. (NB@atconway has pointed out in the comments that private members are not cloned using the JSON method)
编辑(2015 年 1 月 10 日)我想我会重新审视这个,提到我最近开始使用(Newtonsoft)Json 来做到这一点,它应该更轻,并且避免了 [Serializable] 标签的开销。(注意@atconway 在评论中指出私有成员不是使用 JSON 方法克隆的)
/// <summary>
/// Perform a deep Copy of the object, using Json as a serialisation method. NOTE: Private members are not cloned using this method.
/// </summary>
/// <typeparam name="T">The type of object being copied.</typeparam>
/// <param name="source">The object instance to copy.</param>
/// <returns>The copied object.</returns>
public static T CloneJson<T>(this T source)
{
// Don't serialize a null object, simply return the default for that object
if (Object.ReferenceEquals(source, null))
{
return default(T);
}
// initialize inner objects individually
// for example in default constructor some list property initialized with some values,
// but in 'source' these items are cleaned -
// without ObjectCreationHandling.Replace default constructor values will be added to result
var deserializeSettings = new JsonSerializerSettings {ObjectCreationHandling = ObjectCreationHandling.Replace};
return JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(source), deserializeSettings);
}
回答by HappyDude
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.
通常,您实现 ICloneable 接口并自己实现 Clone。C# 对象有一个内置的 MemberwiseClone 方法,该方法执行浅拷贝,可以帮助您处理所有基元。
For a deep copy, there is no way it can know how to automatically do it.
对于深拷贝,它无法知道如何自动执行。
回答by dimarzionist
- Basically you need to implement ICloneable interface and then realize object structure copying.
- If it's deep copy of all members, you need to insure (not relating on solution you choose) that all children are clonable as well.
- Sometimes you need to be aware of some restriction during this process, for example if you copying the ORM objects most of frameworks allow only one object attached to the session and you MUST NOT make clones of this object, or if it's possible you need to care about session attaching of these objects.
- 基本上你需要实现ICloneable接口,然后实现对象结构复制。
- 如果它是所有成员的深层副本,则您需要确保(与您选择的解决方案无关)所有子项也是可克隆的。
- 有时您需要注意此过程中的一些限制,例如,如果您复制 ORM 对象,大多数框架只允许将一个对象附加到会话,并且您不得克隆此对象,或者如果可能您需要注意关于这些对象的会话附加。
Cheers.
干杯。
回答by Nick
I prefer a copy constructor to a clone. The intent is clearer.
我更喜欢复制构造函数而不是克隆。意图更加明确。
回答by Zach Burlingame
The short answer is you inherit from the ICloneable interface and then implement the .clone function. Clone should do a memberwise copy and perform a deep copy on any member that requires it, then return the resulting object. This is a recursive operation ( it requires that all members of the class you want to clone are either value types or implement ICloneable and that their members are either value types or implement ICloneable, and so on).
简短的回答是您从 ICloneable 接口继承,然后实现 .clone 函数。Clone 应该做一个 memberwise 拷贝并对任何需要它的成员执行一个深拷贝,然后返回结果对象。这是一个递归操作(它要求您要克隆的类的所有成员都是值类型或实现 ICloneable,并且它们的成员要么是值类型要么实现 ICloneable,等等)。
For a more detailed explanation on Cloning using ICloneable, check out this article.
有关使用 ICloneable 进行克隆的更详细说明,请查看这篇文章。
The longanswer is "it depends". As mentioned by others, ICloneable is not supported by generics, requires special considerations for circular class references, and is actually viewed by some as a "mistake"in the .NET Framework. The serialization method depends on your objects being serializable, which they may not be and you may have no control over. There is still much debate in the community over which is the "best" practice. In reality, none of the solutions are the one-size fits all best practice for all situations like ICloneable was originally interpreted to be.
该长的答案是“看情况”。正如其他人提到的,ICloneable 不受泛型支持,需要对循环类引用进行特殊考虑,并且实际上被一些人视为.NET Framework 中的“错误”。序列化方法取决于您的对象是否可序列化,它们可能不是,您可能无法控制。社区中仍然存在很多关于哪种“最佳”实践的争论。实际上,没有一个解决方案是像 ICloneable 最初解释的那样适用于所有情况的一刀切最佳实践。
See the this Developer's Corner articlefor a few more options (credit to Ian).
有关更多选项,请参阅此开发人员角文章(归功于 Ian)。
回答by Ryan Lundy
The reason not to use ICloneableis notbecause it doesn't have a generic interface. The reason not to use it is because it's vague. It doesn't make clear whether you're getting a shallow or a deep copy; that's up to the implementer.
不使用的原因ICloneable是不是因为它没有一个通用的接口。 不使用它的原因是因为它含糊不清。不清楚你得到的是浅拷贝还是深拷贝;这取决于实施者。
Yes, MemberwiseClone
makes a shallow copy, but the opposite of MemberwiseClone
isn't Clone
; it would be, perhaps, DeepClone
, which doesn't exist. When you use an object through its ICloneable interface, you can't know which kind of cloning the underlying object performs. (And XML comments won't make it clear, because you'll get the interface comments rather than the ones on the object's Clone method.)
是的,MemberwiseClone
做一个浅拷贝,但相反的MemberwiseClone
不是Clone
;它可能是DeepClone
不存在的。当您通过其 ICloneable 接口使用对象时,您无法知道底层对象执行哪种克隆。(并且 XML 注释不会说清楚,因为您将获得接口注释而不是对象的 Clone 方法上的注释。)
What I usually do is simply make a Copy
method that does exactly what I want.
我通常做的只是制作一个Copy
完全符合我想要的方法。
回答by Daniel Mo?mondor
I came up with this to overcome a .NETshortcoming having to manually deep copy List<T>.
我想出了这个来克服.NET必须手动深度复制 List<T> 的缺点。
I use this:
我用这个:
static public IEnumerable<SpotPlacement> CloneList(List<SpotPlacement> spotPlacements)
{
foreach (SpotPlacement sp in spotPlacements)
{
yield return (SpotPlacement)sp.Clone();
}
}
And at another place:
在另一个地方:
public object Clone()
{
OrderItem newOrderItem = new OrderItem();
...
newOrderItem._exactPlacements.AddRange(SpotPlacement.CloneList(_exactPlacements));
...
return newOrderItem;
}
I tried to come up with oneliner that does this, but it's not possible, due to yield not working inside anonymous method blocks.
我试图提出这样做的 oneliner,但这是不可能的,因为 yield 不能在匿名方法块内工作。
Better still, use generic List<T> cloner:
更好的是,使用通用 List<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();
}
}
}
回答by Michael White
Well I was having problems using ICloneable in Silverlight, but I liked the idea of seralization, I can seralize XML, so I did this:
好吧,我在 Silverlight 中使用 ICloneable 时遇到了问题,但我喜欢序列化的想法,我可以序列化 XML,所以我这样做了:
static public class SerializeHelper
{
//Michael White, Holly Springs Consulting, 2009
//[email protected]
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);
}
}
回答by xr280xr
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.
我也看到它是通过反射实现的。基本上有一种方法可以遍历对象的成员并将它们适当地复制到新对象。当它到达引用类型或集合时,我认为它对自己进行了递归调用。反射很昂贵,但效果很好。
回答by Konstantin Salavatov
Simple extension method to copy all the public properties. Works for any objects and does notrequire class to be [Serializable]
. Can be extended for other access level.
复制所有公共属性的简单扩展方法。适用于任何对象,并且不需要类为[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 ) } );
}
};
}