为什么在原始类型实例上调用Object类的某些函数需要装箱?

10

我发现如果我运行以下代码:

int i = 7;
i.GetHashCode(); //where GetHashCode() is the derived
                 //function from System.Object

没有装箱,但如果我调用i.GetType()(从System.Object派生的另一个函数)替代GetHashCode(),那么将需要进行装箱才能调用GetType()。为什么不可能直接在原始类型实例上调用GetType(),而不需要装箱,而可以调用GetHashCode()而不需要装箱?


我很惊讶GetType被调用了。由于C#是静态类型的,值类型变量不能持有除值类型对象以外的任何东西,因此类型在编译时就可以确定(它是typeof(int))。那么为什么要发出运行时调用呢? - Konrad Rudolph
没错,但这里的意图是要澄清为什么事情没有按照预期工作。 - waheed
3个回答

8
关键在于GetType()不是虚函数,因此无法被重写。由于结构体实际上是sealed的,方法不能被重写,只能像结构体一样处理,所以运行时和编译器可以将已经被重写的结构体方法视为静态调用。
如果您编写一个结构体(很少见),您应该为所有方法覆盖 ToString()Equals()GetHashCode(),正是出于这个原因。如果您没有这样做,它必须进行装箱。然而,GetType() 不能被重写,因此需要进行装箱。
这实际上会导致一些与Nullable<T>和装箱有关的奇怪边缘情况,因为空的Nullable<T>会装箱成null,因此:
int i = obj.GetHashCode(); // fine
Type t = obj.GetType(); // boom

2
我认为原因是GetHashCode直接在System.Int32上实现,您调用System.Int32 :: GetHashCode()。如果调用值类型的已知成员函数,则无需装箱。

那么,那些没有直接在 System.Int32 上实现的函数,比如来自 System.ValueType 或 System.Object 的函数,不能直接调用吗? - waheed
这是ildasm目前向我展示的内容... 当您调用基类函数(如System.Object::GetType())时,需要进行装箱。如果您调用值对象上实现的函数,则不需要装箱... - Arve

1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接