我想我已经知道了一个类的答案,只是想确认我的理解是正确的。假设我有一个 ClassA
及其名为 a
的实例。当 a.MethodA()
被调用时:
(1) CLR通过堆中a
的类型指针找到ClassA
的类型(该类型已经加载到堆)
(2) 查找类型中的MethodA
,如果没有找到,则转到其基类型,直到object
类。
可能我的理解不是很准确,但是我觉得基本是正确的(不对的请指正!)。一个简单的结构的问题来了。
struct MyStruct
{
public void MethodA() { }
}
我有var x = new MyStruct();
,它的值在栈上,MyStruct
的类型已经加载到堆中。当执行x.MethodA()
时,当然没有装箱。 CLR 如何找到 MethodA
并获取 IL 并执行/JIT?我想答案大概是:(再一次,如果我错了请纠正我)
(1) 我们在堆栈上有x
的声明类型。 CLR通过栈上的信息找到它的类型,在它的类型中找到MethodA
。 -- 我们称它为 assumptionA
。
如果您告诉我我的assumptionA
是正确的,我会很高兴。但即使它是错误的,它也说明了一个事实:CLR 有一种无需装箱即可找到结构类型的方法。
现在 x.ToString()
或 x.GetType()
呢?我们知道这个值会被装箱,然后它会像一个类一样执行。但是为什么我们在这里需要拳击呢?既然我们可以得到它的类型(假设A告诉我们),为什么不去它的基类型找到方法(就像一个类)?为什么这里需要昂贵的盒子操作?
最佳答案
假设 A 是错误的。 C# 编译器的符号表存储类型信息。几乎所有情况下都使用静态类型信息,存储在对象中的动态类型仅在类型检查(is
运算符)、转换(as
运算符)和实际转换时需要语法)和数组变体,然后仅当编译器不知道动态类型时。未装箱结构的动态类型始终是静态已知的,并且类实例的动态类型在实例化附近和执行类型检查的条件 block 内是静态已知的(例如,在 if (x is T) y = ( T)x;
该类型在 then 部分内是已知的,因此转换不需要另一次动态检查)。
好的,现在因为 C# 编译器静态地知道 x
的类型,它可以进行重载解析并找到被调用的 exact MethodA。然后它发出 MSIL 以将参数推送到 MSIL 虚拟堆栈并发出包含对该特定方法的元数据引用的调用指令。运行时不需要类型检查。
对于 x.ToString()
,C# 编译器仍然知道它要调用的确切方法。如果 ToString
已被 struct
类型覆盖,它需要一个 pointer-to-MyStruct
类型的参数,编译器无需装箱即可处理。如果未覆盖 ToString
,编译器将生成对 Object.ToString
的调用,它需要一个对象作为其参数。将 x
作为正确的类型推送到 MSIL 虚拟堆栈上需要装箱。
GetType
是一种特殊情况,当类型静态已知时,编译器不会调用任何方法,它只是从符号表中获取类型信息并将元数据引用直接填充到 MSIL 中.
关于c# - CLR 在调用结构的方法时如何工作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5494807/