我想做一些事情,比如:
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();
然后对未反映在原始对象中的新对象进行更改。
我通常不需要这个功能,所以当有必要时,我会创建一个新对象,然后单独复制每个属性,但这总是让我觉得有更好或更优雅的方法来处理这种情况。
如何克隆或深度复制对象,以便在原始对象中不反映任何更改的情况下修改克隆的对象?
当前回答
当使用Marc Gravells protobuf net作为序列化程序时,接受的答案需要一些轻微的修改,因为要复制的对象不会被归因于[Serializable],因此不可序列化,Clone方法将抛出异常。我修改了它以与protobuf net一起使用:
public static T Clone<T>(this T source)
{
if(Attribute.GetCustomAttribute(typeof(T), typeof(ProtoBuf.ProtoContractAttribute))
== null)
{
throw new ArgumentException("Type has no ProtoContract!", "source");
}
if(Object.ReferenceEquals(source, null))
{
return default(T);
}
IFormatter formatter = ProtoBuf.Serializer.CreateFormatter<T>();
using (Stream stream = new MemoryStream())
{
formatter.Serialize(stream, source);
stream.Seek(0, SeekOrigin.Begin);
return (T)formatter.Deserialize(stream);
}
}
这将检查[ProtoControl]属性的存在,并使用protobufs自己的格式化程序来序列化对象。
其他回答
我将使用下面的简单方法来实现这一点。只需创建一个抽象类和实现方法以再次序列化和反序列化并返回。
public abstract class CloneablePrototype<T>
{
public T DeepCopy()
{
string result = JsonConvert.SerializeObject(this);
return JsonConvert.DeserializeObject<T>(result);
}
}
public class YourClass : CloneablePrototype< YourClass>
…
…
…
然后像这样使用它来创建深度副本。
YourClass newObj = (YourClass)oldObj.DeepCopy();
如果您还需要实现浅层复制方法,则此解决方案也很容易扩展。
只需在抽象类中实现一个新方法。
public T ShallowCopy()
{
return (T)this.MemberwiseClone();
}
要克隆类对象,可以使用object.MemberswiseColone方法,
只需将此函数添加到类中:
public class yourClass
{
// ...
// ...
public yourClass DeepCopy()
{
yourClass othercopy = (yourClass)this.MemberwiseClone();
return othercopy;
}
}
然后要执行深度独立复制,只需调用DeepCopy方法:
yourClass newLine = oldLine.DeepCopy();
希望这有帮助。
这将把一个对象的所有可读写财产复制到另一个对象。
public class PropertyCopy<TSource, TTarget>
where TSource: class, new()
where TTarget: class, new()
{
public static TTarget Copy(TSource src, TTarget trg, params string[] properties)
{
if (src==null) return trg;
if (trg == null) trg = new TTarget();
var fulllist = src.GetType().GetProperties().Where(c => c.CanWrite && c.CanRead).ToList();
if (properties != null && properties.Count() > 0)
fulllist = fulllist.Where(c => properties.Contains(c.Name)).ToList();
if (fulllist == null || fulllist.Count() == 0) return trg;
fulllist.ForEach(c =>
{
c.SetValue(trg, c.GetValue(src));
});
return trg;
}
}
这是你使用它的方式:
var cloned = Utils.PropertyCopy<TKTicket, TKTicket>.Copy(_tmp, dbsave,
"Creation",
"Description",
"IdTicketStatus",
"IdUserCreated",
"IdUserInCharge",
"IdUserRequested",
"IsUniqueTicketGenerated",
"LastEdit",
"Subject",
"UniqeTicketRequestId",
"Visibility");
或复制所有内容:
var cloned = Utils.PropertyCopy<TKTicket, TKTicket>.Copy(_tmp, dbsave);
最短的方式,但需要依赖:
using Newtonsoft.Json;
public static T Clone<T>(T source) =>
JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(source));
我提出这一点是为了克服.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,但这是不可能的,因为yield不能在匿名方法块中工作。
更好的是,使用通用的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();
}
}
}