根据调用 ToString() 时对结构进行装箱的规定,在 C# 上调用未重写的方法struct
会导致其被装箱。这最终是因为constrained callvirt
IL 指令的工作方式,根据文档所述:
如果
thisType
是值类型并且thisType
未实现,method
则ptr
取消引用、装箱并作为“this”指针传递给callvirt
方法指令。
但是,它并没有解释为什么会这样,特别是如果struct
确实实现了该方法,它并没有将其装箱struct
:
如果
thisType
是值类型并且thisType
实现了,method
则将ptr
不加修改地作为“this”指针传递给指令,以便call
method
实现。method
thisType
为什么即使该方法未被实现或重写,运行时也不能执行相同的操作struct
?这可能与运行时调用方法的方式有关,但我搞不懂。
简单来说,该方法
object.ToString()
是在假设this
将是 的一个实例的情况下编写的object
。(毕竟,这就是实例方法的工作方式)。您不能将一个指向结构的指针传递给它并期望它能正常工作。所有引用类型都有对象头和方法表。但是结构没有(没有必要)。因此,如果您只是将(指向 a 的指针)结构传递给需要指向 的指针的方法
object
,则该方法可能会以令人兴奋和致命的方式崩溃。当您装箱一个结构体时,它会被复制到堆中,并被赋予一个对象头和方法表。这就是装箱结构体是有效的 的原因
object
。因此,调用需要 ,但传递结构体的方法的方法object
是装箱该结构体。当您在结构本身上定义覆盖时
ToString
,该方法需要一个指向该结构的指针(并且不假定该指针指向方法表),因此您可以直接传递结构而无需装箱。ToString
(实际上,当您在结构中重写时,运行时会发出两种方法。一种是您定义的方法,它期望this
是指向结构的指针,另一种是“thunk”,它期望this
是object
(装箱结构),它会调整this
指针以指向结构本身而不是方法表,然后调用第一个方法。这允许您调用ToString
装箱结构。我不会重复细节,但请参阅Matt Warren 的这篇精彩文章)。