今天早上我的大脑有一个段错误,试图准确理解C#如何以及何时可以从对该对象的引用中计算出对象的类型.请考虑以下高度非原创的示例代码:
class Foo { public virtual void Baz() { } }
class Bar : Foo { }
class Program {
static void Main() {
Foo f = new Bar();
f.Baz();
}
}
Run Code Online (Sandbox Code Playgroud)
引用的类型有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吗?
你的想法太复杂了.
引用'f'是否包含类型信息本身?
不,它没有.它只是Bar前面构造的对象内存开头的地址.该对象包含一个虚方法表(可能还有对其关联Type对象1的引用,但这与此无关).
当我打电话给f.Baz()时,我是否认为通过Bar的MethodTable进行调度?
是.
是否只是这种情况,C#编译器使用流量分析来计算正在发生的事情并防止任何非法操作?
流量分析很复杂,完全没必要.编译器允许完全允许声明类型的操作f- 即Foo.编译器根本不关心实际(=动态)类型f.
你能告诉我们最终的声明来自最终的IL吗?
要看.一个对象不具有静态类型,因此"讲述了在运行时静态类型"是没有意义的.另一方面,声明有所不同.如果变量是方法的形式参数,那么您可以(在运行时)使用反射来确定方法参数的声明类型.
对于局部变量,此操作再次毫无意义.在另一方面中,所述IL 确实存储该信息(作为元数据?),通过.locals这样的代码可以理论上进行逆向工程(丝丝和反射器做到这一点),以获得静态类型的变量.
1我在这里猜测,但这实际上是不可能的.如果每个对象都拥有自己对相关Type对象的引用,那将意味着指针的额外开销.另外,这个引用完全没有必要,因为对象可以简单地调用GetType自己来获取它的类型.GetType只需要为每个类实现一次(实际上是一种静态方法),并通过通常的虚函数表调度.因此,每个类只需要一个对a的引用Type,而不是每个对象.