究竟什么是CLR参考,它如何保存类型信息?

时间:2010-11-08 12:43:26

标签: c# clr

我的大脑今天早上有一个段错误,试图准确理解C#如何以及何时可以从对该对象的引用中计算出对象的类型。请考虑以下高度非原创的示例代码:

class Foo { public virtual void Baz() { } }
class Bar : Foo { }

class Program {
    static void Main() {
        Foo f = new Bar();
        f.Baz();
    }
}

引用的类型有Foo,但实际创建的对象实例是Bar。该Bar实例有一些开销,即同步块索引,以及对MethodTable的引用,可能是Bar的MethodTable。如果查看堆上的Bar对象,其类型的唯一线索是MethodTable引用,这表明它是一个Bar。

然后问题。有没有办法让C#从实际的对象图中知道'f'是Foo,如果是,怎么样?引用'f'是否包含类型信息本身?当我打电话给f.Baz()时,我是否认为通过Bar的MethodTable进行调度?是否只是这种情况,C#编译器使用流量分析来计算正在发生的事情并防止任何非法操作? CLR在被翻译成IL时真的不关心Foo的类型声明吗?

道歉,如果这是一个冗长且措词不清的问题 - 请告诉我是否需要澄清!

TL; DR - CLR中的多态引用如何工作?如何保持实际与声明的类类型之间的差异,并且您能告诉最终的声明来自结果IL吗?

2 个答案:

答案 0 :(得分:5)

你的想法太复杂了。

  

引用'f'本身是否包含类型信息?

没有。它不必。它只是前面构造的Bar对象内存开头的地址。 对象包含一个虚方法表(可能还有对其关联的Type对象 1 的引用,但这与此无关。)

  

当我打电话给f.Baz()时,我是否认为通过Bar的MethodTable进行调度是正确的?

  

C#编译器是否使用流量分析来计算出现的情况并防止任何非法操作?

流量分析很复杂,完全没必要。编译器准确允许f的声明类型允许的那些操作 - Foo。编译器根本不会关注关于f的实际(=动态)类型。

  

你能告诉最终的声明来自最终的IL吗?

取决于。 对象具有静态类型,因此“在运行时告诉其静态类型”是没有意义的。另一方面,声明有所不同。如果变量是方法的形式参数,那么您可以(在运行时)使用反射来确定方法参数的声明类型。

对于局部变量,此操作再次毫无意义。另一方面,IL 通过.locals 存储此信息(作为元数据?),因此代码 理论上可以进行逆向工程(Cecil和Reflector执行此操作) )获取静态类型的变量。


1 我在这里猜测,但这实际上是不可能的。如果每个对象都拥有自己对相关Type对象的引用,那么这将意味着指针的额外开销。此外,此引用完全没有必要,因为对象可以简单地调用GetType来获取其类型。 GetType只需要为每个类实现一次(实际上是一种静态方法),并通过通常的虚函数表调度。因此,每个类只需要一个Type的引用,而不是每个对象。

答案 1 :(得分:1)

  

C#有没有办法从实际的对象图中知道'f'是Foo

编译器静态知道

  

引用'f'本身是否包含类型信息?

有趣的是,它必须以某种方式包含在IL中。

  

当我打电话给f.Baz()时,我是否正确地认为发送是通过Bar的MethodTable进行的?

是。这是通过f指向的实例找到的。