c#Reference-Type by ref - 地址到地址?

时间:2017-06-19 19:50:45

标签: c# pointers reference pass-by-reference

如果我通过引用传递Reference-Type,那么

试图理解究竟发生了什么。我这样理解:

所以我有一个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

来更改它指向的位置或将指针从堆栈杀死到堆栈

这是对的吗?非常感谢你!

2 个答案:

答案 0 :(得分:1)

  

我只是传递"地址变量/参考变量"的地址。 f。因此,g实际上指向堆栈中的f,但不指向堆中的实际对象。

实际上你倒退了。当您不使用ref时,g的值将为f(在您的示例中为0x2A53BC 已复制 到名为g的新变量。 gf完全相互独立,但它们都拥有"值" 0x2A53BC。当您执行g = null;时,变量f仍将保留0x2A53BC,但g现在将保留0x000000

注意:我不确定下一部分,我可能会告诉你事实上不正确的信息,但我正在描述外部观察者的正确行为。
当您使用ref时,变量g将指向f的值的地址(比如说0x154AFA),而f仍指向{{} 1}}。当您执行0x2A53BC g = null;的值未更新时,它仍然指向g,但0x154AFA现在将指向f

答案 1 :(得分:0)

因此Fooclass类型,特别是引用类型。你有:

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会被0x000000null的神奇表现形式)覆盖。当方法返回时,甚至在返回之前,f已将其值更改为指向新的"地点" 0x000000

总结:"价值"参考类型是参考。当按值传递时,引用"数字"的副本由调用的方法制作和使用。通过引用传递时,不会复制该值,callee方法和调用方法使用相同的存储位置。

补充:来自C#规范的一些引用:

  

5.1.4值参数

     

声明没有refout修饰符的参数是a    值参数

     

值参数出现   在调用函数成员(方法,实例构造函数,   访问者,或运算符)或参数的匿名函数   属于,并使用给定的参数值初始化   调用。返回时,值参数通常不再存在   函数成员或匿名函数。 [...]

     

5.1.5参考参数

     

使用ref修饰符声明的参数是 参考参数

     

一个   reference参数不会创建新的存储位置。相反,一个   reference参数表示与之相同的存储位置   变量作为函数成员或匿名中的参数给出   函数调用。因此,参考参数的值是   始终与基础变量相同。 [...]