我一直在努力彻底了解参考和价值类型。就在我以为自己拥有它时,我遇到了这种情况...
我创建了一个包含单个对象的类。
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;
正如预期的那样,当我打印出a.A和b.A的值时,它们是相同的。
Console.WriteLine("a.A={0},b.A={1}",a.A,b.A);
//a.A=1,b.A=1
并且,正如预期的那样,当我更改a.A的值时,b.A的值也会因为它们引用相同的对象而发生变化。
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的值不像以前那样改变。
有人可以解释为什么这个场景中的作业与之前的作业有所不同吗?
由于
答案 0 :(得分: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
张纸 - 它仍然有旧地址,如果你访问那个房子,你会发现没有任何改变。它只是只是写在纸上的内容已经改变了。
这有帮助吗?
答案 1 :(得分:3)
不同之处在于封装对象Container
。
在第一种情况下,您有一个包含引用的对象。当你说你创建了Container
类的新实例时,你却没有。您刚刚复制了对现有对象的引用。由于您有两个对同一对象的引用,您可以通过一个引用更改对象的内容,并通过另一个引用读取它。
a b a b
\ / \ /
\ / \ /
--------- ---------
| | | |
| A | | A |
| | | | | |
----|---- -> ----|----
| |
--------- ---------
| | | |
| 1 | | 2 |
| | | |
--------- ---------
在第二种情况下,您还有两个引用两个相同的对象,但在这种情况下,您直接引用盒装对象,而不是容器。为其中一个引用分配新值时,将获得两个单独的对象。一个对象是盒装1,另一个对象是盒装2.当你为b分配一个新值时,它不会将值放在它指向的框中,它将创建一个包含新值的新盒装对象
a b a b
\ / | |
\ / | |
--------- -> --------- ---------
| | | | | |
| 1 | | 1 | | 2 |
| | | | | |
--------- --------- ---------
答案 2 :(得分:2)
在第一个例子中你有这个:
a b
\ /
\ /
| |
v v
(Container)
这里只有一个容器实例。这两个变量都引用了这个容器。当你改变容器时,两个变量都会看到变化。
但是在这段代码中:
object c = 1;
object d = c;
第二个赋值并不意味着“d是c的别名”,它只是意味着在第二个赋值后c
和d
指向同一个对象。
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)
答案 3 :(得分:0)
在第一个场景中,您有一个对堆上的一个对象的引用。你有两个变量(“a”,“b”)引用同一个对象。这就是为什么当您更改此对象的成员时,您会看到它反映在两个变量上。
在你的第二种情况下,你正在做一些完全不同的事情。当你这样做时:
c = 2
您实际创建了一个全新的对象。 int的值类型被转换为对象,因此创建了一个新的引用对象。此时,您的“d”变量不再引用与“c”变量相同的对象。