MemberwiseClone() 和在 C# 中分配引用类型有什么区别?

What is the difference between MemberwiseClone() and Assigning a Reference type in C#?

如果我有这样的代码

class Student
{
    public string RollID { get; set; }
}
class Person
{
    public Student student { get; set; }
    public string Address { get; set; }
    public string Name { get; set; }
    public Person Clone()
    {
        return (Person)this.MemberwiseClone();
    }
}
class Client
{

    static void Main()
    {
        Student s1 = new Student();
        s1.RollID = "151";
        Person p1 = new Person();
        p1.Address = "bombay";
        p1.Name = "foo";
        p1.student = s1;

        Person p2 = p1.Clone();
        p2.student.RollID = "1558";

        Person p3 = p1;
        p3.student.RollID = "454";
    }
}

当我更改 p2 的值时,它也会更改 p1 的值,当我更改对象 p3 的值时,我得到相同的结果。我的问题是,如果两个逻辑都做同样的事情,那么分配和使用 MemberwiseClone() 方法之间的真正区别是什么。如果我使用 MemberwiseClone() 方法还有其他优势吗?

C# classes 默认处理 "by ref"。这意味着当您将某些东西分配给另一个 class 而不是结构时,您只需将指针传递给新对象,而新对象在内存中具有两个句柄的同一位置。另一方面,您可以复制 class。当您成员明智地克隆某些东西时,您会创建一个新对象,该对象在内存中占据另一个位置。

现在这个Object.MemberwiseClone()方法是一个浅拷贝函数,它只复制class和其中的结构,class里面的classes将被保留参考。参见 this

要深度复制一些 class 你需要专门为那个 class 实现它,或者你可以找到使用反射编写的通用的,这有点慢。

这里的问题是您正在对浅表副本进行 "deep" 更新。如果你改了p2.Address,不会影响p1.Address;但是如果你改变 p3.Address 它会改变影响 p1.Address.

但是由于 p1 和 p2 共享对单个 Student 的引用,因此更改 RollId 会影响到每个人。

Variable P1 ---->  <PERSON OBJECT 1>
                   |       .Address = 123 Elm St.
Variable P3 ---->  |
                   |       .Student  ----> <STUDENT OBJECT 1>
                   |_______________        |
                                           | .RollId // one value for all
Variable P2 ---->  <PERSON OBJECT 2>       |
                   |       .Address = ...  |
                   |       .Student  ----> |_________________
                   |_______________

MemberwiseClone 方法通过创建一个新对象,然后将当前对象的非静态字段复制到新对象来创建浅拷贝。如果字段是值类型,则执行该字段的逐位复制。如果字段是引用类型,则复制引用但不复制引用的对象;因此,原始对象及其克隆对象指的是同一个对象。

https://msdn.microsoft.com/en-us/library/system.object.memberwiseclone(v=vs.85).aspx

有两种克隆:浅克隆和深克隆。

在浅克隆中,副本中的任何引用值都引用与原始对象中相同的对象。 在深度克隆中,新对象的引用值被设置为新对象。 要进行深度克隆,请实现 ICloneable 接口 return New Person 对象。