Когда именно обнуляемые типы генерируют исключения? - PullRequest
0 голосов

Рассмотрим следующий код:

int? x = null;
Console.Write ("Hashcode: ");
Console.WriteLine(x.GetHashCode());
Console.Write("Type: ");
Console.WriteLine(x.GetType());

При выполнении он пишет, что хэш-код равен 0, но завершается неудачно с NullReferenceException при попытке определить тип x. Я знаю, что методы, вызываемые для обнуляемых типов, на самом деле вызываются для базовых значений, поэтому я ожидал, что во время x.GetHashCode().

произойдет сбой программы.

Итак, в чем принципиальная разница между этими двумя методами и почему первый из них не выходит из строя?

Ответы [ 4 ]

0 голосов
/ 02 мая 2018

Чтобы уточнить правильный ответ Дэнни Чена:

  • 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(), вы бы увидели исключение.

0 голосов
/ 02 мая 2018

Реализация Nullable<T>.GetHashCode() выглядит следующим образом:

public override int GetHashCode()
{
    if (!this.HasValue)
    {
        return 0;
    }
    return this.value.GetHashCode();
}

Таким образом, когда значение равно нулю, оно всегда даст вам 0.

x.GetType() совпадает с null.GetType(), который выдаст Object reference not set to an instance of an object

0 голосов
/ 02 мая 2018

Это связано с тем, что int? x = null; по существу создает экземпляр типа значения System.Nullable<int> со "внутренним" значением null (вы можете проверить его с помощью свойства .HasVaue). Когда вызывается GetHashCode, переопределение Nullable<int>.GetHashCode является кандидатом в метод (поскольку метод является виртуальным), теперь у нас есть экземпляр Nullable<int>, и мы выполняем его метод экземпляра, perfect.

При вызове GetType метод является не виртуальным, поэтому экземпляр Nullable<int> сначала помещается в System.Object, в соответствии с документом , а значение в штучной упаковке - null, следовательно NullReferenceException.

0 голосов
/ 02 мая 2018

Похоже, что GetHashCode имеет нулевую проверку. (Использовал JetBrains для просмотра определения)

public override int GetHashCode()
{
  if (!this.hasValue)
    return 0;
  return this.value.GetHashCode();
}
...