3

我无法理解下面给出的两组代码片段的输出。怎么没有真正理解浅拷贝的概念。怎么解释?

班级:

 public class Person : ICloneable
 {
    public string Name;        
    public int[] arr; 
    public object Clone()
    {
        return this.MemberwiseClone();
    }
 }

代码片段 1:

 static void Main(string[] args)
    {
        Person p1 = new Person();
        p1.Name = "Name1";
        p1.arr = new int[5] {1,2,3,4,5 };
        Person p2 = (Person)p1.Clone();

        p1.Name = "Name2";
        p1.arr[0] = 11;

        Console.WriteLine(p2.Name);
        Console.WriteLine(p2.arr[0].ToString());
        Console.Read();

    }

输出:名称1 11

怀疑:字符串不是引用类型。那么为什么 p2.Name 在片段 1 中打印为“Name1”

代码片段 2:

static void Main(string[] args)
    {
        Person p1 = new Person();
        p1.Name = "Name1";
        p1.arr = new int[5] { 1, 2, 3, 4, 5 };
        Person p2 = (Person)p1.Clone();

        p1.Name = "Name2";
        p1.arr = new int[5] { 11, 12, 13, 14, 15 };

        Console.WriteLine(p2.Name);
        Console.WriteLine(p2.arr[0].ToString());
        Console.Read();

    }

输出:名称1 1

4

4 回答 4

4

您示例中的 int[] 数组是引用类型。这意味着,两者都p1.arr指向p2.arr内存中的同一个数组。

如果更改 的第一个索引的值p1.arr,这意味着 的第一个索引的p2.arr值也会更改。因此,代码片段 1 中的行为。

第二个代码片段的不同之处在于您更改了对 p1 数组的引用。现在,p1.arr是对新对象的引用。p2.arr仍然持有对“原始”数组的引用。因此,打印p2.arr[0]打印 1。

编辑

希望消除一些疑问,如果您记得键入以下内容,可能会更清楚:

p1.Name = "Name2";

实际上是:

p1.Name = new String("Name2");

这与您的 int[] 数组完全相同。您没有更改 的值p1.Name,而是创建了一个新的字符串对象,并将p1.Name的引用更改为这个新的字符串对象。p2.Name 仍然拥有自己对“原始”字符串对象的引用,即“Name1”。通过更改 的引用p1.Name引用不会改变。

于 2009-04-08T12:59:43.857 回答
2

为了减轻您对原始问题的怀疑。

String 确实是一种引用类型,要记住的是,你对数组所做的事情和你对字符串所做的事情是不一样的。

p1.Name = "Name2"; // new String - 等价于 p1.Name = new string("Name2")
p1.arr[0] = 11;//更新数组元素

对于数组,您正在更改引用的内存中的数据。对于字符串,您正在创建一个新字符串(在新的内存位置)并使 p1.Name (引用)指向新分配的内存。p2.Name(这是一个不同的引用)仍然指向存储字符“Name1”的原始内存位置

顺便说一句,由于字符串的不变性,p1.Name 的更改不会出现在 p2.Name 中。任何更改字符串的尝试,例如 string.replace,都会在内存中创建一个新字符串。

希望有帮助。

于 2009-04-08T13:30:00.803 回答
2

来自http://msdn.microsoft.com/en-us/library/system.object.memberwiseclone.aspx

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

于 2009-04-08T12:49:26.440 回答
0

请看内联评论:

static void Main(string[] args)
{
    Person p1 = new Person();
    p1.Name = "Name1";
    p1.arr = new int[5] {1,2,3,4,5 };
    Person p2 = (Person)p1.Clone();

    p1.Name = "Name2"; //Now p1.Name points to a new memory location
    //But p2.Name is still pointing to the location p1.Name had
    // originally pointed to.

    p1.arr[0] = 11; //here p1.arr and p2.arr are pointing to the same place
    //So since you are changing the value of one location it gets 
    //reflected in both

    Console.WriteLine(p2.Name); //Prints Name1
    Console.WriteLine(p2.arr[0].ToString()); //Prints 11
    Console.Read();

}

在你说的第二个片段中

p1.arr = new int[5] { 11, 12, 13, 14, 15 };

p1.arr 指向一个全新的位置。(就像你做 p1.Name = "Name2" 时会发生什么)所以它没有反映在 p2.arr 上,它仍然指向 p1.arr 之前指向的同一个地方。(即到数组 {1,2,3,4,5})

于 2009-04-08T19:29:43.513 回答