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

4
如果我有如下代码:
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()方法,还有其他优势吗?


1
尝试更改名称或地址,您将获得不同的结果。 - Charles Mager
1
MemberwiseClone 只复制顶层属性,不进行递归。 - Daniel A. White
不要使用MemberwiseClone,除非你确切知道正在发生什么。 - Jonesopolis
@CharlesMager Name和Address是值类型,所以我会得到不同的结果,但我的问题是关于我已更改的引用类型的值,如果两种逻辑都给出相同的结果,那么介绍MemberwiseClone()方法的优势是什么? - Lijin Durairaj
1
@LijinJohn 不对,string 是一个引用类型。克隆操作已经复制了你的属性值。你仍然拥有同一个 Student 实例的引用,这就是为什么更新它的属性会影响到两个副本的原因。 - Charles Mager
1
如果你在p2上更改了Student属性的值,p1将不会受到影响(就像更改地址一样)。但这不是你所做的;你改变了由Student属性引用的对象的属性。克隆的优点是你可以得到一个新的对象,但这并不意味着你得到了一个全新的对象图。 - Mark Adelsberger
3个回答

5
MemberwiseClone方法创建一个浅表副本,它会创建一个新对象,并将当前对象的非静态字段复制到新对象中。如果字段是值类型,则执行一位一位的复制操作。如果字段是引用类型,则复制引用但不复制所引用的对象;因此,原始对象和其克隆体引用同一个对象。
有两种类型的克隆:浅表克隆和深度克隆。
在浅表克隆中,副本中的任何引用值都引用与原始对象中相同的对象。 在深度克隆中,新对象的引用值设置为新对象。为了进行深度克隆,实现ICloneable接口并返回一个新的Person对象。
链接: https://msdn.microsoft.com/en-us/library/system.object.memberwiseclone(v=vs.85).aspx

5
问题在于你对浅拷贝进行了“深度”更新。如果你更改了p2.Address,它不会影响p1.Address;但是如果你更改了p3.Address,它将会影响到p1.Address。
但由于p1和p2共享一个学生的引用,更改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  ----> |_________________
                   |_______________

2
默认情况下,C#类被“按引用”处理。这意味着当你把一个类而非结构体赋值给另一个对象时,你只是将指针移交给新对象,并且两个句柄占用同一块内存。另一方面,你可以复制一个类。当你逐个成员地克隆某个对象时,你会创建一个新的对象,在内存中占据另一个位置。

现在,Object.MemberwiseClone()方法是一个浅拷贝函数,它仅仅拷贝了该类和结构体以及其中的类(内部)的引用。参见此链接

如果要深度复制某个类,则需要为该类编写特定的实现,或者可以找到使用反射编写的通用实现,但这可能会比较慢。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接