我想做一些事情,比如:
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();
然后对未反映在原始对象中的新对象进行更改。
我通常不需要这个功能,所以当有必要时,我会创建一个新对象,然后单独复制每个属性,但这总是让我觉得有更好或更优雅的方法来处理这种情况。
如何克隆或深度复制对象,以便在原始对象中不反映任何更改的情况下修改克隆的对象?
当前回答
如果使用net.core并且对象是可序列化的,则可以使用
var jsonBin = BinaryData.FromObjectAsJson(yourObject);
then
var yourObjectCloned = jsonBin.ToObjectFromJson<YourType>();
BinaryData在dotnet中,因此您不需要第三方库。它还可以处理类上的属性为Object类型的情况(属性中的实际数据仍然需要可序列化)
其他回答
我在Silverlight中使用ICloneable时遇到了问题,但我喜欢序列化的想法,我可以序列化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);
}
}
在一个方法中重铸怎么样基本上应该调用自动复制构造函数
T t = new T();
T t2 = (T)t; //eh something like that
List<myclass> cloneum;
public void SomeFuncB(ref List<myclass> _mylist)
{
cloneum = new List<myclass>();
cloneum = (List < myclass >) _mylist;
cloneum.Add(new myclass(3));
_mylist = new List<myclass>();
}
似乎对我有用
代码生成器
我们已经看到了很多想法,从序列化到手动实现再到反射,我想使用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 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;
}
}
注意:最新版本有更多的空检查,但为了更好地理解,我省略了它们。
最短的方式,但需要依赖:
using Newtonsoft.Json;
public static T Clone<T>(T source) =>
JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(source));
简单的答案是从ICloneable接口继承,然后实现.clone函数。克隆应该按成员进行复制,并对任何需要它的成员执行深度复制,然后返回结果对象。这是一个递归操作(它要求您要克隆的类的所有成员都是值类型或实现ICloneable,并且它们的成员是值类型或者实现IClonea,依此类推)。
有关使用ICloneable克隆的详细说明,请参阅本文。
长篇大论的答案是“视情况而定”。正如其他人所提到的,ICloneable不受泛型支持,需要对循环类引用进行特殊考虑,并且实际上被一些人视为.NET Framework中的“错误”。序列化方法取决于对象是否可序列化,而这些对象可能不可序列化,并且您可能无法控制。对于哪种做法是“最佳”做法,社会上仍有很多争论。事实上,没有一个解决方案是适用于所有情况的一刀切的最佳实践,就像ICloneable最初被解释的那样。
请参阅本开发者角文章,了解更多选项(归功于Ian)。