Когда именно типы nullable генерируют исключения?
Рассмотрим следующий код:
int? x = null;
Console.Write ("Hashcode: ");
Console.WriteLine(x.GetHashCode());
Console.Write("Type: ");
Console.WriteLine(x.GetType());
При выполнении он пишет, что Hashcode равен 0
, но с ошибкой NullReferenceException
в попытке определить тип x
. Я знаю, что методы, называемые нулевыми типами, на самом деле вызываются базовыми значениями, поэтому я ожидал, что программа будет терпеть неудачу во время x.GetHashCode()
.
Итак, какова принципиальная разница между этими двумя методами и почему первая из них не сработает?
Ответы
Ответ 1
Это потому, что int? x = null;
int? x = null;
по существу создает экземпляр типа значения System.Nullable<int>
, с "внутренним" значением null
(вы можете проверить его с помощью .HasVaue
). Когда вызывается GetHashCode
, переопределяющий Nullable<int>.GetHashCode
является кандидатом метода (поскольку метод является виртуальным), теперь у нас есть экземпляр Nullable<int>
, и его метод экземпляра отлично.
При вызове GetType
метод не является виртуальным, поэтому экземпляр Nullable<int>
сначала System.Object
в System.Object
, в соответствии с документом, а значение в NullReferenceException
равно null
, следовательно, NullReferenceException
.
Ответ 2
Чтобы уточнить Дэнни Чена правильный ответ:
-
Nullable<T>
- тип значения. Тип значения состоит из bool, который указывает на недействительность (false означает null) и значение T. - В отличие от всех других типов значений, типы с
Nullable<T>
значением не привязываются к коробке Nullable<T>
. Они боксировать либо коробочную T
или нулевую ссылку. - Метод, реализуемый значением типа
S
, реализуется так, как будто он имеет невидимый аргумент ref S
; вот как this
передается. - Метод, реализованный ссылочным типом
C
, реализуется так, как если бы существовал невидимый аргумент C
; вот как this
передается. - Интересным случаем является виртуальный метод, определенный в базовом базовом классе и переопределяемый структурой, наследующей от базового класса.
Теперь у вас достаточно информации, чтобы узнать, что происходит. GetHashCode
виртуальный и переопределены Nullable<T>
так что, когда вы называете это, вы называете его, как будто там была невидимая ref Nullable<T>
аргумент для this
. Никакого бокса не происходит.
GetType
не является виртуальным и поэтому не может быть переопределен и определен на object
. Поэтому он ожидает object
для this
Когда Nullable<T>
приемник должен быть помещен в коробку и, следовательно, может иметь значение null и, следовательно, может выбрасывать.
Если вы вызвали ((object)x).GetHashCode()
вы увидите исключение.
Ответ 3
Реализация Nullable<T>.GetHashCode()
выглядит следующим образом:
public override int GetHashCode()
{
if (!this.HasValue)
{
return 0;
}
return this.value.GetHashCode();
}
Итак, когда значение равно null, оно всегда будет вам 0
.
x.GetType()
аналогичен null.GetType()
который будет null.GetType()
Object reference not set to an instance of an object
Ответ 4
Кажется, что GetHashCode имеет нулевую проверку. (Используется JetBrains для просмотра defenition)
public override int GetHashCode()
{
if (!this.hasValue)
return 0;
return this.value.GetHashCode();
}