Почему null оператор ToString() возвращает пустую строку?
Мне просто интересно, в чем разница между двумя следующими утверждениями:
-
Вызывает исключение NullReferenceException - это нормально.
object test1 = default(int?);
object result = test1.ToString();
-
Возвращает пустую строку "", почему?
object test2 = default(int?).ToString();
-
Это то же самое, что и 2.
int? test3 = null;
if (test3.ToString() == string.Empty) //returns true
{
Console.WriteLine("int? = String.Empty, isn't it strange?").
}
-
И просто для удовольствия - я могу доказать, что bool может быть равно int value (hmmm, как? bool может быть только ложным, или true, и int никогда не может быть таким).
if (default(int?).ToString() == default(bool?).ToString()) //returns true because both are empty strings
{
Console.WriteLine("int = bool");
}
Примечание: default (int?) возвращает null.
Ответы
Ответ 1
An int?
является Nullable<int>
. Nullable
- это struct, что делает его типом значения, а не ссылочным типом. test3
на самом деле не установлен на null
. Присвоение ему null
на самом деле приводит к созданию новой структуры, которая просто имеет поле HasValue
этой структуры для false
вместо true
. (Для такого неявного преобразования требуется специальная поддержка компилятора, поэтому вы не могли бы использовать свой собственный тип MyNullable<T>
.)
Поскольку объект с нулевым значением имеет фактическое значение, он может вызывать ToString
и предоставлять содержательное значение, в данном случае пустую строку.
Когда вы установите это значение с нулевым значением, поместив его в переменную object
, это приведет к тому, что в этой переменной будет сохранено фактическое значение null
, поэтому вы получаете NRE при вызове метода на нем.
Когда тип с нулевым значением вставляется в бокс, он не вводит тип значения NULL; вместо этого, если он не имеет значения, он присваивает объекту null
, если он имеет значение, то базовое значение разворачивается и затем помещается в бокс. (Для этого требуется специальная поддержка из среды выполнения, что является еще одной причиной, по которой вы не можете создать свой собственный тип MyNullable<T>
.)
Ответ 2
В C используется токен .
для доступа к элементу левого операнда и ->
для доступа к элементу объекта, в который левый операнд содержит указатель (ссылку). Нулевая ссылка возникает, если левый операнд ->
имеет значение null. В С# значение .
имеет прежнее значение при применении к типам значений, а последнее - при применении к типам классов; используя .
в С# в переменной типа класса, которая равна null, вызывается NullReferenceException
.
Тип .NET Nullable<T>
представляет собой структуру с двумя полями, которая содержит логическое поле, которое указывает, имеет ли значение значение (не нуль), а поле типа значения T
, чтобы сказать, что это за значение (если не ноль). Хотя компилятор позволит назначить Nullable<T>
null или сравнить его с нулевым значением, null
в первом случае на самом деле является просто сокращением экземпляра по умолчанию (где флаг "имеет значение" является ложным, а value имеет значение по умолчанию для типа T), а сравнение с null - это просто сокращение для проверки свойства HasValue
(которое, в свою очередь, смотрит в поле Boolean
). Вызов ToString
напр. a Nullable<int>
, который является "нулевым", не отличается семантически от вызова его на любую другую структуру, которая содержит Boolean
и Int32
, которые имеют значения False
и 0
соответственно. Существует несколько способов, с помощью которых компиляторы обрабатывают типы с нулевым значением "специально", но значение Nullable<int>
по умолчанию содержит [False, 0]
, а не нулевую ссылку.
Обратите внимание, что первый пример выбрасывается из-за одного из "специальных" правил, которые применяются к типам с нулевым значением. Передача значения типа Object
или другого ссылочного типа обычно дает указание среде выполнения создать объект, который будет вести себя как объект класса с полями и членами, соответствующими строкам и возвращать ссылку на этот новый объект - процесс, известный как "заниматься боксом". Поскольку разработчики типов с нулевым значением хотели разрешить коду говорить if (NullableThing == null)
, а не (IMHO sematic clearer) if (!NullableThing.HasValue)
, среда выполнения была реализована так, чтобы бокс с нулевым значением, чье поле HasValue
было ложным, даст нулевую ссылку, а не ссылка на экземпляр Nullable<T>
по умолчанию. Хотя я думаю, что есть случаи, когда было бы целесообразно использовать некоторые типы значений, используя разные методы box и unbox, типы с нулевым значением являются единственными, которые могут использовать что-либо, кроме обычного бокса.