我无法理解下面给出的两组代码片段的输出。 怎么没有真正得到浅拷贝的概念。怎么解释?
类别:
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
答案 0 :(得分: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
的引用, 引用不会更改。
答案 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})