Чтобы уточнить правильный ответ Дэнни Чена:
Nullable<T>
является типом значения. Тип значения состоит из значения типа bool, которое указывает на нулевое значение (false означает ноль), и на значение T.
- В отличие от всех других типов значений, обнуляемые типы не заключаются в квадрат
Nullable<T>
. Они указывают либо в штучной упаковке T
, либо на нулевую ссылку.
- Метод, реализованный типом значения
S
, реализован так, как будто он имеет невидимый аргумент ref S
; вот как this
передается.
- Метод, реализованный ссылочным типом
C
, реализован так, как если бы был невидимый аргумент C
; вот как this
передается.
- Интересный случай - это виртуальный метод, определенный в базовом классе ссылки и переопределенный структурой, которая наследуется от базового класса.
Теперь у вас достаточно информации, чтобы понять, что происходит. GetHashCode
является виртуальным и переопределяется на Nullable<T>
, поэтому, когда вы вызываете его, вы вызываете его, как если бы был невидимый аргумент ref Nullable<T>
для this
. Бокса не бывает.
GetType
не является виртуальным и поэтому не может быть переопределено и определено в object
. Следовательно, он ожидает object
для this
. При вызове на Nullable<T>
получатель должен быть упакован, и, следовательно, может установить на ноль и, следовательно, может выбросить.
Если бы вы позвонили ((object)x).GetHashCode()
, вы бы увидели исключение.