我想做一些事情,比如:

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

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

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

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


当前回答

在一个方法中重铸怎么样基本上应该调用自动复制构造函数

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

似乎对我有用

其他回答

对于克隆过程,可以先将对象转换为字节数组,然后再转换回对象。

public static class Extentions
{
    public static T Clone<T>(this T obj)
    {
        byte[] buffer = BinarySerialize(obj);
        return (T)BinaryDeserialize(buffer);
    }

    public static byte[] BinarySerialize(object obj)
    {
        using (var stream = new MemoryStream())
        {
            var formatter = new BinaryFormatter(); 
            formatter.Serialize(stream, obj); 
            return stream.ToArray();
        }
    }

    public static object BinaryDeserialize(byte[] buffer)
    {
        using (var stream = new MemoryStream(buffer))
        {
           var formatter = new BinaryFormatter(); 
           return formatter.Deserialize(stream);
        }
    }
}

必须为序列化进程序列化对象。

[Serializable]
public class MyObject
{
    public string Name  { get; set; }
}

用法:

MyObject myObj  = GetMyObj();
MyObject newObj = myObj.Clone();

在大量阅读了这里链接的许多选项以及这个问题的可能解决方案之后,我相信所有选项都在Ian P的链接中得到了很好的总结(所有其他选项都是这些选项的变体),Pedro77的问题评论链接提供了最佳解决方案。

所以我将在这里复制这两个参考文献的相关部分。这样我们就可以:

在C sharp中克隆对象的最佳方法!

首先,这些是我们的所有选择:

手动使用ICloneable,它是浅层的,不属于类型安全MemberwiseColone,它使用ICloneable使用Activator.CreateInstance和递归MemberwiseClone进行反射正如johnc的首选答案所指出的序列化中级语言,我不知道它是如何工作的扩展方法,如Havard Straden的自定义克隆框架表达式树

通过表达式树进行快速深度复制一文还对通过序列化、反射和表达式树进行克隆的性能进行了比较。

为什么选择ICloneable(即手动)

Venkat Subramaniam先生(此处为冗余链接)详细解释了原因。

他的所有文章都围绕着一个例子,试图适用于大多数情况,使用三个对象:人、大脑和城市。我们想克隆一个人,这个人将拥有自己的大脑,但同样的城市。你可以想象上面任何其他方法都会带来的所有问题,也可以阅读本文。

这是我对他的结论稍作修改的版本:

通过指定New后跟类名来复制对象通常会导致代码不可扩展。使用克隆(原型模式的应用)是实现这一点的更好方法。然而,使用C#(和Java)中提供的克隆也会有很大的问题。最好提供一个受保护的(非公共的)复制构造函数,并从clone方法调用它。这使我们能够将创建对象的任务委托给类本身的实例,从而提供可扩展性,并使用受保护的复制构造函数安全地创建对象。

希望这一实现能够让事情变得清晰:

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

现在考虑从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();
    }
}

您可以尝试运行以下代码:

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

产生的输出将是:

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

注意,如果我们对对象的数量进行计数,这里实现的克隆将保持正确的对象数量计数。

我在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);
    }
}

最短的方式,但需要依赖:

using Newtonsoft.Json;
    public static T Clone<T>(T source) =>
        JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(source));

除了这里的一些精彩答案外,您可以在C#9.0及更高版本中完成以下操作(假设您可以将类转换为记录):

record Record
{
    public int Property1 { get; set; }

    public string Property2 { get; set; }
}

然后,只需使用with运算符将一个对象的值复制到新对象。

var object1 = new Record()
{
    Property1 = 1,
    Property2 = "2"
};

var object2 = object1 with { };
// object2 now has Property1 = 1 & Property2 = "2"

我希望这有助于:)