Подтвердить что ты не робот

Типы ссылок против Nullable типов ToString()

Может ли кто-то быть достаточно любезен, чтобы объяснить, почему вызов ToString() в пустом ссылочном типе вызывает исключение (которое, на мой взгляд, имеет смысл, вы не можете вызвать метод ни с чем!), но называя ToString() на пустой Nullable(Of T) возвращает String.Empty? Это было для меня неожиданностью, поскольку я предположил, что поведение будет последовательным по всем типам.

Nullable<Guid> value = null;
Stock stock = null;
string result = value.ToString(); //Returns empty string
string result1 = stock.ToString(); //Causes a NullReferenceException
4b9b3361

Ответ 1

Nullable<T> на самом деле является struct, у которого есть поддержка поддержки и поддержки компилятора, чтобы вести себя как a null, фактически не будучи null.

То, что вы видите, - это столкновение между реализацией, позволяющее вам естественно рассматривать ее как null, как и любой другой ссылочный тип, но позволяющий вызвать вызов метода, потому что Nullable<T> не является фактически нулевым, значение внутри него равно нулю.

Визуально похоже, что он не должен работать, это просто потому, что вы не можете видеть, что делается в фоновом режиме для вас.

Другой такой визуальный обман можно увидеть, когда вы вызываете метод расширения в нулевом ссылочном типе... работает вызов (против визуального ожидания), потому что под капотом он разрешен в вызов статического метода, передающий ваш нулевой экземпляр как параметр.

Как работает Nullable <T> тип работы за кулисами?

Ответ 2

Nullable - это тип значения, а присваивание null вызывает его инициализацию с помощью Value=null и HasValue=false.

Далее, Nullable.ToString() реализуется следующим образом:

public override string ToString()
{
    if (!this.HasValue)
    {
        return "";
    }
    return this.value.ToString();
}

Итак, ожидается то, что вы видите.

Ответ 3

Это немного сложно с типами с нулевым значением. Когда вы установите его на null, это не актуально, а не null, потому что это не ссылочный тип (это тип значения). Когда вы инициализируете эту переменную с помощью null, она создает новый экземпляр sctructure, где свойство HasValue равно false, а Value - null, поэтому, когда вы вызываете метод ToString, он хорошо работает на экземпляре структуры.

Ответ 4

Исключение, вызванное вызовом default(object).ToString(), называется NullReferenceException по какой-либо причине, оно вызывает метод с нулевой ссылкой. default(int?), с другой стороны, не является нулевой ссылкой, потому что это не ссылка; это тип значения со значением, эквивалентным null.

Большая практическая точка заключается в том, что если это было сделано, то следующее не получится:

default(int?).HasValue // should return false, or throw an exception?

Это также испортило бы способ, с помощью которого мы могли бы смешивать значения nullables и non-nullables:

((int?)null).Equals(1) // should return false, or throw an exception?

И следующее становится совершенно бесполезным:

default(int?).GetValueOrDefault(-1);

Мы могли бы избавиться от HasValue и сравнить усилия с нулем, но тогда, если переопределение равенства типа значения, которое сделало nullable, может возвращать true в сравнении с null в некоторых случаях. Это не может быть отличной идеей, но это может быть сделано, и язык должен справиться.

Давайте вспомним, почему вводятся типы с нулевым значением. Возможность того, что ссылочный тип может быть нулевым, присуща концепции ссылочных типов, если не предпринимаются усилия для обеспечения непустоты: ссылочные типы - это типы, которые ссылаются на что-то, и это подразумевает возможность того, что никто не ссылается ни на что, что мы называем null.

Несмотря на то, что во многих случаях это неприятно, мы можем использовать это в различных случаях, таких как представление "неизвестного значения", "нет допустимого значения" и т.д. (мы можем использовать его для того, что означает нуль в базах данных, например).

В этот момент мы дали значение null в данном контексте за пределами простого факта, что данная ссылка не относится ни к какому объекту.

Так как это полезно, мы могли бы хотеть установить значение int или DateTime равным null, но мы не можем, потому что это не типы, которые относятся к чему-то другому, и, следовательно, не могут быть в состояние не ссылаясь ни на что больше, чем я, как млекопитающее, может потерять свои перья.

Типы с нулевым значением, введенные с 2.0, дают нам форму типов значений, которые могут иметь семантический нуль, с помощью другого механизма, чем тип ссылочных типов. Большинство из этого вы могли бы закодировать сами, если бы этого не существовало, но специальные правила бокса и продвижения позволяют использовать более разумный бокс и использование оператора.

Хорошо. Теперь рассмотрим, почему NullReferenceExceptions происходит в первую очередь. Два неизбежны, и одно из них было конструктивным решением в С# (и не относится ко всем .NET).

  • Вы пытаетесь вызвать виртуальный метод или свойство или получить доступ к полю с нулевой ссылкой. Это должно потерпеть неудачу, потому что нет способа поиска того, что должно быть вызвано переопределение, и никакого такого поля.
  • Вы вызываете не виртуальный метод или свойство по нулевой ссылке, которая в свою очередь вызывает виртуальный метод или свойство или обращается к полю. Это, очевидно, вариант в первой части, но решение по дизайну, которое мы приступаем к следующему, имеет преимущество, гарантирующее, что это не сработает с самого начала, а не частично (что может быть запутанным и иметь долгосрочные побочные эффекты).
  • Вы вызываете не виртуальный метод или свойство на нулевую ссылку, которая не вызывает виртуальный метод или свойство, или доступ к полю. Там нет неотъемлемой причины, почему это не должно быть разрешено, и некоторые языки позволяют это, но на С# они решили использовать callvirt, а не call, чтобы заставить NullReferenceException для согласованности (не могу сказать, что я согласен, но там вы идете).

Ни один из этих случаев никоим образом не применим к типу значений с нулевым значением. Невозможно поместить тип нулевых значений в условие, в котором нет способа узнать, какое поле или метод переопределить для доступа. Вся концепция NullReferenceException просто не имеет смысла здесь.

В целом, не выбрасывание NullReferenceException , совместимое с другими типами, - типы через него, если и только если используется нулевая ссылка.

Обратите внимание, что есть случай, когда вызов нулевого типа с нулевым типом, он делает это с помощью GetType(), потому что GetType() не является виртуальным, а при вызове типа значения всегда подразумевается бокс. Это справедливо для других типов значений, поэтому:

(1).GetType()

рассматривается как:

((object)1).GetType()

Но в случае нулевых типов бокс превращает те с ложным HasValue в null, и, следовательно,

default(int?).GetType()

рассматривается как:

((object)default(int?)).GetType()

в результате чего GetType() вызывается на нулевом объекте и, следовательно, бросается.

Это, кстати, приводит нас к тому, почему не фальсификация NullReferenceType была более разумным дизайнерским решением - люди, которые нуждаются в таком поведении, всегда могут быть в коробке. Если вы хотите его использовать, используйте ((object)myNullableValue).GetString(), поэтому нет необходимости в том, чтобы язык рассматривал его как особый случай для принудительного исключения.

ИЗМЕНИТЬ

О, я забыл упомянуть о механике за NullReferenceException.

Тест для NullReferenceException очень дешев, потому что он в основном просто игнорирует проблему, а затем выхватывает исключение из ОС, если это происходит. Другими словами, не является тестом.

См. Что представляет собой реализация CLR за сборкой/генерированием исключения нулевой ссылки? и обратите внимание на то, как ни одно из них не будет работать с типами значений с нулевым значением.

Ответ 5

Если вы исследуете определение Nullable<>, существует переопределение определения ToString. В этой функции ToString переопределяется для возврата String.Empty.

    // Summary:
    //     Returns the text representation of the value of the current System.Nullable<T>
    //     object.
    //
    // Returns:
    //     The text representation of the value of the current System.Nullable<T> object
    //     if the System.Nullable<T>.HasValue property is true, or an empty string ("")
    //     if the System.Nullable<T>.HasValue property is false.
    public override string ToString();

С другой стороны, Stock - это пользовательский класс, который, как я полагаю, ToString не переоценивается. Таким образом, он возвращает исключение NullReferenceException, поскольку он использует поведение по умолчанию.

Ответ 6

В соответствии с примечаниями MSDN

Метод Guid.ToSTring() Возвращает строковое представление значение этого экземпляра Guid, в соответствии с предоставленным форматом Спецификатор.

В соответствии с MSDN Замечания по Nullable

Тип считается нулевым, если ему может быть присвоено значение или может быть присвоенный null, что означает, что тип не имеет никакого значения. Следовательно, тип с нулевым значением может выражать значение или что никакое значение существует. Например, ссылочный тип, такой как String, имеет значение NULL, тогда как тип значения, такой как Int32, не является. Тип значения не может быть nullable, поскольку он обладает достаточной емкостью для выражения только значений подходящий для этого типа; он не имеет дополнительной емкости требуется для выражения значения null.