我想做一些事情,比如:

MyObject myObj = GetMyObj(); // Create and fill a new object
MyObject newObj = myObj.Clone();

然后对未反映在原始对象中的新对象进行更改。

我通常不需要这个功能,所以当有必要时,我会创建一个新对象,然后单独复制每个属性,但这总是让我觉得有更好或更优雅的方法来处理这种情况。

如何克隆或深度复制对象,以便在原始对象中不反映任何更改的情况下修改克隆的对象?


当前回答

通常,您实现ICloneable接口并自己实现克隆。C#对象有一个内置的MemberwiseColone方法,该方法执行浅层复制,可以帮助您处理所有原语。

对于深度复制,它无法知道如何自动执行。

其他回答

通常,您实现ICloneable接口并自己实现克隆。C#对象有一个内置的MemberwiseColone方法,该方法执行浅层复制,可以帮助您处理所有原语。

对于深度复制,它无法知道如何自动执行。

我将使用下面的简单方法来实现这一点。只需创建一个抽象类和实现方法以再次序列化和反序列化并返回。

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();
}

创建扩展:

public static T Clone<T>(this T theObject)
{
    string jsonData = JsonConvert.SerializeObject(theObject);
    return JsonConvert.DeserializeObject<T>(jsonData);
}

这样称呼:

NewObject = OldObject.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自己的格式化程序来序列化对象。

基本上,您需要实现ICloneable接口,然后实现对象结构复制。如果它是所有成员的深度拷贝,您需要确保(与您选择的解决方案无关)所有子级都是可克隆的。有时,在这个过程中,您需要注意一些限制,例如,如果您复制ORM对象,大多数框架只允许一个对象附加到会话,并且您不能克隆该对象,或者如果可能,您需要关注这些对象的会话附加。

干杯