浅拷贝 - 参考类型异常性质

时间:2009-04-08 12:45:03

标签: c# shallow-copy

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

类别:

 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 个答案:

答案 0 :(得分:4)

示例中的int []数组是引用类型。这意味着,p1.arrp2.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的引用, 引用不会更改。

答案 1 :(得分:2)

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

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

答案 2 :(得分:2)

减轻您对原始问题的疑虑。

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

p1.Name = "Name2"; // new String -equivalent to p1.Name = new string("Name2")
p1.arr[0] = 11; //updated array element

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

由于字符串的 immutability ,因此无法在p2.Name中显示对p1.Name的更改。任何更改字符串的尝试(例如string.replace)都会在内存中创建一个新字符串。

希望有所帮助。

答案 3 :(得分: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})