试图理解究竟发生了什么。我这样理解:
所以我有一个Class Foo,它代表一个Reference-Type
class Foo{ //Foo-stuff here }
所以这就在这里
Foo f = new Foo();
Foo f
(在堆栈上创建)保存实际Object的地址/引用,在调用new Foo()
时在堆中创建;
因此f
实际上包含0x2A53BC
如果我调用方法这样做
void Test(Foo g) { g = null; }
我只是传递"地址变量/参考变量"的地址。 f
。所以g
实际指向堆栈中的f
,但不指向堆中的实际对象。所以我可以通过f
更改堆中Foo对象的属性,因为我的地址为f
,我可以通过f
访问Foo,但设置g=null
只会杀死Stack中f
变量的地址,而不是Heap本身对象的引用。
但是有了这个
void Test(ref Foo g) { g = null; }
我实际上正在使用堆中的Foo-Object的真实地址,因此使用0x2A53BC
。这意味着,我可以通过设置g=null
这是对的吗?非常感谢你!
答案 0 :(得分:1)
我只是传递"地址变量/参考变量"的地址。
f
。因此,g
实际上指向堆栈中的f
,但不指向堆中的实际对象。
实际上你倒退了。当您不使用ref
时,g的值将为f
(在您的示例中为0x2A53BC
) 已复制 到名为g
的新变量。 g
和f
完全相互独立,但它们都拥有"值" 0x2A53BC
。当您执行g = null;
时,变量f
仍将保留0x2A53BC
,但g现在将保留0x000000
。
注意:我不确定下一部分,我可能会告诉你事实上不正确的信息,但我正在描述外部观察者的正确行为。
当您使用ref
时,变量g
将指向f
的值的地址(比如说0x154AFA
),而f
仍指向{{} 1}}。当您执行0x2A53BC
g = null;
的值未更新时,它仍然指向g
,但0x154AFA
现在将指向f
答案 1 :(得分:0)
因此Foo
是class
类型,特别是引用类型。你有:
var f = new Foo();
所以f
的值实际上是实际对象的引用。正如你所说,我们可能会将引用视为一个整数,比如0x2A53BC
(只是为了使它具体化,我们实际上无法访问整数或引用真正的;这些引用没有指针算法。)
现在,当你传递值时,就像重载一样:
void Test(Foo g) { g = null; }
我们使用Test(f);
调用,值0x2A53BC
将 复制 到新的存储位置(堆栈上的新位置) ,新的存储位置变为g
。如果您使用g
来改变现有实例,那么由于两个存储位置都包含相同的信息0x2A53BC
,因此您可能会改变由f
指向的实例。但您选择分配给g
。这导致g
的新存储位置现在保持引用0x000000
(我们简单地假设零用作null
的表示)。它显然不会更改f
的存储位置,该位置仍然指向位于0x2A53BC
的原始实例。
相反,与另一个重载:
void Test(ref Foo g) { g = null; }
我们使用Test(ref f);
调用,我们通过引用传递。因此g
使用了相同的存储位置,就像我们已经为f
所使用的那样。不复制值0x2A53BC
。当您指定g = null;
时,原始 0x2A53BC
会被0x000000
(null
的神奇表现形式)覆盖。当方法返回时,甚至在返回之前,f
已将其值更改为指向新的"地点" 0x000000
。
总结:"价值"参考类型是参考。当按值传递时,引用"数字"的副本由调用的方法制作和使用。通过引用传递时,不会复制该值,callee方法和调用方法使用相同的存储位置。
补充:来自C#规范的一些引用:
5.1.4值参数
声明没有
ref
或out
修饰符的参数是a 值参数 。值参数出现 在调用函数成员(方法,实例构造函数, 访问者,或运算符)或参数的匿名函数 属于,并使用给定的参数值初始化 调用。返回时,值参数通常不再存在 函数成员或匿名函数。 [...]
5.1.5参考参数
使用
ref
修饰符声明的参数是 参考参数 。一个 reference参数不会创建新的存储位置。相反,一个 reference参数表示与之相同的存储位置 变量作为函数成员或匿名中的参数给出 函数调用。因此,参考参数的值是 始终与基础变量相同。 [...]