参考和值类型方案

我一直在努力彻底了解参考和价值类型。 就在我以为自己拥有它的时候,我遇到了这个场景……

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

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("aA={0},bA={1}",aA,bA); //aA=1,bA=1 

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

 aA = 2; Console.WriteLine("aA={0},bA={1}",aA,bA); // aA=2,bA=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的值不像以前那样改变。

有人可以解释为什么这个场景中的作业与之前的作业有所不同吗?

谢谢

这是你的第一个错误:

我创建了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来引用不同的对象。 如果打印出cd的值,它们将分别是“不同的字符串”和“第一个字符串”。 更改c的值不会更改d的值


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

 aA = 2; 

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

让我们用一个真实世界的比喻来使这更容易。 假设我们所有的变量都是纸上写有房屋地址的。 aA = 2;的变化aA = 2; 就像在那个地址改变房子的内容一样。 当然,在他们的纸上写有相同地址的任何人都会看到这一变化。

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

这有帮助吗?

不同的是封装对象Container

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

  abab \ / \ / \ / \ / --------- --------- | | | | | A | | A | | | | | | | ----|---- -> ----|---- | | --------- --------- | | | | | 1 | | 2 | | | | | --------- --------- 

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

  abab \ / | | \ / | | --------- -> --------- --------- | | | | | | | 1 | | 1 | | 2 | | | | | | | --------- --------- --------- 

在第一个例子中你有这个:

  ab \ / \ / | | vv (Container) 

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

但是在这段代码中:

 object c = 1; object d = c; 

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

 c = 2; 

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

  Before After cdcd \ / c=2 | | \ / ----> | | | | | | vvvv (boxed integer 1) (boxed integer 2) (boxed integer 1) 

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

在你的第二种情况下,你做的事情完全不同。 你做了什么:

 c = 2 

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