Когда именно типы 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();
}