就像字符串一样,对象类型是不可变的吗?

时间:2018-03-28 15:18:01

标签: c#

我试图理解值类型和引用类型之间的区别,并且怀疑我无法理解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

3 个答案:

答案 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 变量指向它们 - 旁边两个您开始使用的对象(但现在已被“放弃”)。