引用类型在内部是如何工作的?

3
public class ReferenceType
{
   public int FName { get; set; }        
   public int LName { get; set; }        
}  

ReferenceType rt1;          //Line 6
rt1 = new ReferenceType();   //Line 7 >>have just split a single line statement<<

众所周知,在上述代码场景中引用类型是如何工作的。

  • rt1 在栈中分配了一部分内存,以保存将来某个对象的引用(第6行)。

  • rt1 被赋予了一个在堆上创建的对象的引用 [a specific]。

那么,这个引用在内部实际上是什么样子的呢?rt1 是否像 C++ 中的指针一样保存着对象的地址或十六进制数?还是其他什么东西?


1
我认为这个问题太过宽泛,涉及到了太多CLR实现细节。类似于C++指针的东西(我不明白你所说的十六进制是什么意思)。实际上,你最好称其为“句柄”而不是指针(因为对象可能会被GC移动到内存中)。 - Adriano Repetti
C#引用和指针有什么区别? - Soner Gönül
2
您可能会发现这个有用:http://www.codeproject.com/Articles/20481/NET-Type-Internals-From-a-Microsoft-CLR-Perspecti - NoChance
这个问题之前已经被问过(并且得到了回答):http://stackoverflow.com/questions/5920636/how-a-reference-in-c-sharp-is-implemented-internally?rq=1 - StevieB
@adriano 十六进制是指内存中的地址位置,就像我们在C++中获取的那样... - x-code
显示剩余2条评论
1个回答

4
您的第一个要点是不正确的。
引用被添加到堆栈上。然后,这个引用引用了堆内存。
(免责声明:以下语句是我认为它是如何工作的...在SSCLI中肯定是这样的)
引用被实现为指针...但没有正式要求说明它们必须是指针。这可能会随时在Microsoft的自由裁量下更改。它们(目前)指向对象的方法表。
一个对象看起来像这样:
       +-------------+
       |             |
       |  Reference  |
       |             |
       +------+------+
              |
              |                +-------------------------------+
              |                |          Object header        |
              +--------------->|-------------------------------|
                               |                               |
                               |                               |
                               |                               |
                               |           Method table        |
                               |                               |
                               |                               |
                               |                               |
                               |                               |
                               +-------------------------------+
包含同步块索引和指向对象类型定义的指针。需要知道的是,引用知道它们指向的内容,但指针并不一定知道。CLR强制执行这一点。

没错,这是一个实现细节,您不必担心它。但我认为了解它没有任何问题。

只要您不依赖于实现细节,了解它并不会对您造成伤害。


1
+1... 嗯,我刚想起来了(堆栈)的那个点... - x-code
方法表?我不能发誓(可能会更改),但它不应该指向那里。它应该(曾经)指向一个_handles_表(在那里,引用另一个指针,您将拥有方法表)。就像您所说的,这甚至不是C ++指针实现的方式(至少在我知道的编译器中)。 - Adriano Repetti
我只是猜测:指向引用的固定指针将访问其字段,然后它将指向方法表之外的其他内容。如果转储,则其值与引用本身不同。最后,“调用”在JITed时不需要解除引用方法表,但“callvirt”需要。双重解除引用(对象本身和虚拟方法)是其速度较慢的原因之一。然后,对象引用不应直接指向方法,而应指向字段(它们不必一起增长)。再次强调,这只是我的猜测... - Adriano Repetti
我不确定你所说的“固定指向引用的指针将访问其字段,然后它将指向方法表之外的其他内容”的意思。如果您指向一个字段,内部只是一个(this + 4) + fieldOffset来跳过方法表到特定实例字段(或在64位系统上为+8)...并且它只是该地址的值被推入一个新指针。call不会解引用..因为引用已经指向了方法表。同样,这是一个简单的偏移跳转到所需的方法调用(或者如果它还没有被JIT编译,则是thunk)。 - Simon Whitehead
在SO提供的格式下讨论这个问题真的很困难:/ 我认为我们基本上是在谈论同一件事情..? - Simon Whitehead
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接