我试图理解值类型和引用类型之间的区别,并且怀疑我无法理解Person类(用户定义数据类型)和对象类型之间的差异。
当我Person p1 = new Person();p1.Name = "Ashu";
“Ashu”将被存储在堆中并且在p2 = p1;
之后,p2将查看此内存,并且使用p2的任何更改也将更改p1,因为两者都指向相同的内存。
// Just a demo which shows data changed for ref type
Person p1 = new Person();
Person p2 = new Person();
p1.Id = 10;
p2 = p1;
p2.Id = 20;
Console.WriteLine(p1.Id); // Output 20
为什么以上示例不适用于以下示例,因为对象也是引用类型
// Same example when type is "Object"
object obj1 = new object();
object obj2 = new object();
obj1 = 10;
obj2 = obj1;
obj2 = 20;
Console.WriteLine(obj1); // Output 10
答案 0 :(得分:6)
右;让我们这样做!
Person p1 = new Person();
我们现在有一个变量和一个Person对象(默认情况下Id = 0)
p1 ---------------> [Person #0, Id = 0]
Person p2 = new Person();
我们现在有2个变量和2个对象(默认情况下Id = 0)
p1 ---------------> [Person #0, Id = 0]
p2 ---------------> [Person #1, Id = 0]
p1.Id = 10;
此通过p1
取消引用对象,并在最后设置ID:
p1 ---------------> [Person #0, Id = 10]
p2 ---------------> [Person #1, Id = 0]
p2 = p1;
此使用 p1 中的值覆盖p2 的引用。在此操作中没有创建或销毁任何对象,但现在它们都指向同一个对象:
p1 ---------------> [Person #0, Id = 10]
p2 -------------------/|\
(注意另一个对象仍然存在于某个地方,但是我们无法再达到它; GC会找到它并很快杀死它)
p2.Id = 20;
此通过p2中的引用取消引用对象。我们只拥有一个可到达的对象,所以我们不应该对自己发现自己感到惊讶:
p1 ---------------> [Person #0, Id = 20]
p2 -------------------/|\
Console.WriteLine(p1.Id); // Output 20
通过p1
指向同一个对象的解除引用 ,因此我们输出20:
p1 ---------------> [Person #0, Id = 20]
p2 -------------------/|\
object obj1 = new object();
这将创建一个新的System.Object实例,并将对它的引用分配给obj1
:
obj1 ---------------> [Object #0]
object obj2 = new object();
这也是同样的事情 - 另一个新的Object
和新变量:
obj1 ---------------> [Object #0]
obj2 ---------------> [Object #1]
obj1 = 10;
这会创建另一个新对象 - 这次是一个盒装整数:
obj1 ---------------> [Int32 #0, value 10]
obj2 ---------------> [Object #1]
obj2 = obj1;
复制此参考:
obj1 ---------------> [Int32 #0, value 10]
obj2 --------------------/|\
obj2 = 20;
创建另一个新对象并分配给obj2
:
obj1 ---------------> [Int32 #0, value 10]
obj2 ---------------> [Int32 #1, value 20]
最后
Console.WriteLine(obj1); // Output 10
deferences obj1
并找到值10:
obj1 ---------------> [Int32 #0, value 10]
obj2 ---------------> [Int32 #1, value 20]
不同之处在于,在第一个版本中,我们在最后讨论同一个对象;在第二个版本中,我们正在讨论两个不同的对象(碰巧是不可变的,但这不是重点;如果我们有一个可变对象,我们仍然会看到这种行为;区别在于:分配引用与变异属性< em> via 参考)。
答案 1 :(得分:1)
当你像这样使用=
时:
p2 = p1;
您正在更改右侧包含的变量。在上述情况下,p2
现在存储的引用指向与p1
中的引用相同的对象。
修改对象的某些属性时:
p2.Id = 20;
您没有更改p2
包含的内容。相反,您正在更改p2
所指的对象。
这正是为什么这不适用于int
的原因。您无法设置int
的任何属性。这是不可改变的。即使它是可变的并且暴露了一些属性供你设置,你也不会看到这种效果,因为你不能用object
类型的变量设置该属性。
你现在正在做的是:
obj2 = 20;
正如我之前所说,使用=
这样做会直接改变obj2
。这意味着您正在obj2
指向其他内容,这不是obj1
所指向的内容!
答案 2 :(得分:1)
对于语句recycle
,编译器确定10 不一个对象。它将语句视为obj1 = 10;
。
执行此操作时,会在堆上创建包含 10 的新对象。
对于obj1 = (object)10;
,编译器确定obj1与obj2的类型相同,因此只执行指针赋值。
然后obj2 = obj1;
发生了,并且在堆上创建了包含 20 的新对象,原因与上述相同。
最后,堆上有两个新对象,一个包含obj2 = 20;
,另一个包含10
,每个都有 1 变量指向它们 - 旁边两个您开始使用的对象(但现在已被“放弃”)。