下面是书中的代码示例,用于显示何时对值类型进行装箱:
internal struct Point
{
private readonly Int32 m_x, m_y;
public Point(Int32 x, Int32 y) {
m_x = x;
m_y = y;
}
//Override ToString method inherited from System.ValueType
public override string ToString() {
return String.Format("({0}, {1})", m_x.ToString(), m_y.ToString());
}
}
class Program
{
static void Main(string[] args) {
Point p1 = new Point(10, 10);
p1.ToString();
}
}
作者说:
In the call to ToString, p1 doesn’t have to be boxed. At first, you’d think that p1 would have to be boxed because
ToString
is a virtual method that is inherited from the base type, System.ValueType. Normally, to call a virtual method, the CLR needs to determine the object’s type in order to locate the type’s method table. Because p1 is an unboxed value type, there’s no type object pointer. However, the just-in-time (JIT) compiler sees that Point overrides the ToString method, and it emits code that calls ToString directly (nonvirtually) without having to do any boxing. The compiler knows that polymorphism can’t come into play here because Point is a value type, and no type can derive from it to provide another implementation of this virtual method.
我有点明白它的意思了,因为Point
从System.ValueType
重写了ToString
,CLR不需要检查类型对象来定位类型的方法表,编译器可以发出直接调用 ToString 的 IL 代码。很公平。
但假设 p1
还从 System.ValueType
调用 GetHashCode
,如下所示:
class Program
{
static void Main(string[] args) {
Point p1 = new Point(10, 10);
p1.ToString();
p1.GetHashCode();
}
}
由于 Point
结构不会从 System.ValueType
重写 GetHashCode()
,因此编译器这次无法直接发出 IL 代码,并且 CLR需要定位类型的方法表来查找GetHashCode
方法,但是正如作者所说,p1是一个未装箱的值类型,没有类型对象指针,那么CLR如何查找GetHashCode堆中
方法?Point
结构体类型对象中的
最佳答案
如果我们查看生成的 MSIL,我们会看到以下内容:
IL_0000: ldloca.s 00 // p1
IL_0002: ldc.i4.s 0A
IL_0004: ldc.i4.s 0A
IL_0006: call System.Drawing.Point..ctor
IL_000B: ldloca.s 00 // p1
IL_000D: constrained. System.Drawing.Point
IL_0013: callvirt System.Object.ToString
IL_0018: pop
IL_0019: ldloca.s 00 // p1
IL_001B: constrained. System.Drawing.Point
IL_0021: callvirt System.Object.GetHashCode
IL_0026: pop
我们查一下ECMA-335 Part III.2.1在约束上。
:
The constrained. prefix is permitted only on a callvirt instruction. The type of ptr must be a managed pointer (&) to thisType. The constrained prefix is designed to allow callvirt instructions to be made in a uniform way independent of whether thisType is a value type or a reference type.
If thisType is a value type and thisType implements method then
ptr
is passed unmodified as the ‘this’ pointer to a call of method implemented by thisType
If thisType is a value type and thisType does not implement method then
ptr
is dereferenced, boxed, and passed as the ‘this’ pointer to the callvirt of method
This last case can only occur when method was defined on
System.Object
,System.ValueType
, orSystem.Enum
and not overridden by thisType. In this last case, the boxing causes a copy of the original object to be made, however since all methods onSystem.Object
,System.ValueType
, andSystem.Enum
do not modify the state of the object, this fact cannot be detected.
所以,是的,这确实会导致装箱,但仅当没有覆盖时,因为 System.Object
方法需要一个类,而不是一个值类型。但如果它被重写,则该方法的 this
指针必须是托管指针,与任何其他值类型方法相同。
关于c# - 如何使用堆中的类型对象来定位结构体实例的虚拟方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65931791/