4

我一直在尝试彻底理解 Reference 和 Value 类型。就在我以为我有它的时候,我遇到了这个场景......

我创建了一个包含单个对象的类。

class Container
{
    public object A {get; set;}
}

当我创建此 Container 类的实例时 (a) 我正在创建引用类型的实例。我为类中的对象分配了一个整数。据我所知,这将被装箱为一个对象,另一种引用类型。

int start = 1;  
Container a = new Container{ A=start };

我创建了 Container 类 (b) 的另一个实例,但是将第一个容器的值分配给它,b 的值现在是对 a 的引用。

Container b = a;

正如预期的那样,当我打印出 aA 和 bA 的值时,它们是相同的。

Console.WriteLine("a.A={0},b.A={1}",a.A,b.A);
//a.A=1,b.A=1

而且,正如预期的那样,当我更改 aA 的值时,bA 的值也会因为它们引用同一个对象而发生变化。

a.A = 2;

Console.WriteLine("a.A={0},b.A={1}",a.A,b.A);
// a.A=2,b.A=2

现在我决定尝试使用单个本地对象。同样,我将整数装箱到第一个对象中,并将第一个对象的值分配给第二个对象。我相信此时的对象应该是引用类型,因此 c 和 d 应该引用同一个对象。在不更改任何内容的情况下,它们返回相同的值。

int start = 1;
object c = start;
object d = c;

Console.WriteLine("c={0},d={1}",c,d);
// c=1,d=1

和以前一样,在更改初始对象的值时,我希望两个对象的值相同。

c = 2;

Console.WriteLine("c={0},d={1}",c,d);
// c=2,d=1

当我打印这两个对象的结果时, d 的值不会像以前那样改变。

有人可以解释为什么在这种情况下的分配与以前的不同吗?

谢谢

4

4 回答 4

10

这是你的第一个错误:

我创建了 Container 类 (b) 的另一个实例,但是将第一个容器的值分配给它,b 的值现在是对 a 的引用。

Container b = a;

不是创建另一个实例。它声明了另一个变量。现在两个变量都指向同一个对象。

(我会在继续阅读时继续编辑这个答案......)

接下来:

int start = 1;
object c = start;
object d = c;
Console.WriteLine("c={0},d={1}",c,d); // c=1,d=1

和以前一样,在更改初始对象的值时,我希望两个对象的值相同。

c = 2;
Console.WriteLine("c={0},d={1}",c,d); // c=2,d=1

那不是在改变一个对象,而是在改变一个变量。每个分配都复制一个值 - 除了其中一个还执行装箱操作。让我们稍微简化一下:

object c = "first string";
object d = c;

现在没有涉及拳击 - 它只会让它更容易理解。

两个变量当前都有引用同一个对象的值。这是由于分配,但没有其他任何东西可以链接这两个变量。它们目前恰好具有相同的值,但它们是自变量。现在让我们改变一个:

c = "different string";

这改变了指向不同对象的值。c如果你打印出它们的值,c它们d将分别是“不同的字符串”和“第一个字符串”。更改 的值c不会更改 的值d


现在,让我们回到您之前的场景,看看为什么它会有所不同。在那里,你有:

a.A = 2;

这根本不会改变 的价值a。它正在改变对象a所指的数据。

让我们用一个现实世界的类比来使这更容易。假设我们所有的变量都是写有家庭地址的纸片。的改变a.A = 2;就像改变那个地址的房子的内容。当然,任何在纸上写有相同地址人都会看到变化。

现在想想你的c/d场景。再一次,假设我们有两张纸,并且由于赋值运算符,它们都写有相同的地址。现在你为变量本身分配一个新值c就像擦掉c纸上的地址并在上面写一个不同的地址。这根本不会改变那d张纸——它仍然有旧地址,如果你访问那所房子,你会发现什么都没有改变。只是纸上写的东西变了。

这有帮助吗?

于 2010-07-17T19:07:01.057 回答
3

不同之处在于封装对象Container

在第一种情况下,您有一个包含引用的对象。当您说您创建了Container该类的新实例时,您没有。您刚刚复制了对现有对象的引用。由于您对同一对象有两个引用,因此您可以通过一个引用更改对象的内容并通过另一个引用读取它。

 a     b          a     b
  \   /            \   /
   \ /              \ /
---------        ---------
|       |        |       |
|   A   |        |   A   |
|   |   |        |   |   |
----|----   ->   ----|----
    |                |
---------        ---------
|       |        |       |
|   1   |        |   2   |
|       |        |       |
---------        ---------

在第二种情况下,您也有两个引用两个相同的对象,但在这种情况下,您直接引用装箱的对象,而不是容器。当您为其中一个引用分配新值时,您将获得两个单独的对象。一个对象是boxed 1,另一个对象是boxed 2。当你给b分配一个新值时,它不会把值放在它指向的box中,它会创建一个包含新值的新boxed object .

 a     b             a          b
  \   /              |          |
   \ /               |          |
---------   ->   ---------  ---------
|       |        |       |  |       |
|   1   |        |   1   |  |   2   |
|       |        |       |  |       |
---------        ---------  ---------
于 2010-07-17T19:14:12.770 回答
2

在第一个示例中,您有以下内容:

 a      b  
  \    /  
   \  /  
   |  |  
   v  v  
(Container)

这里只有一个容器实例。这两个变量都引用了这个容器。当你改变容器时,两个变量都会看到变化。

但是在这段代码中:

object c = 1;
object d = c;

第二个赋值并不意味着“d 是 c 的别名”,它只是意味着在第二个赋值之后cd指向同一个对象。

c = 2;

现在你重新分配c给一个新的装箱整数,所以c现在d指向两个不同的对象。这两个对象没有任何关系。

 Before                 After

 c      d               c                    d
  \    /         c=2    |                    |
   \  /          ---->  |                    |
   |  |                 |                    |
   v  v                 v                    v
(boxed integer 1)      (boxed integer 2)    (boxed integer 1)
于 2010-07-17T19:07:51.897 回答
0

在您的第一个场景中,您引用了堆上的一个对象。你有两个变量(“a”,“b”)指的是同一个对象。这就是为什么当您更改此对象的成员时,您会看到它反映在两个变量上。

在第二种情况下,您正在做一些完全不同的事情。当你这样做时:

c = 2

您实际上创建了一个全新的对象。int 的值类型被转换为对象,因此创建了一个新的引用对象。此时,您的“d”变量不再指代与“c”变量相同的对象。

于 2010-07-17T19:08:25.687 回答