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

Каким может быть поведение Nullable <T> в боксе/распаковке?

Что-то мне пришло в голову раньше сегодня, и я почесал голову.

Любую переменную типа Nullable<T> можно присвоить null. Например:

int? i = null;

Сначала я не мог понять, как это возможно, без какого-либо определения неявного преобразования от object до Nullable<T>:

public static implicit operator Nullable<T>(object box);

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

int? i = new object();

Тогда я понял, что, возможно, тип Nullable<T> может определить неявное преобразование в какой-либо произвольный ссылочный тип, который никогда не может быть создан, например:

public abstract class DummyBox
{
    private DummyBox()
    { }
}

public struct Nullable<T> where T : struct
{
    public static implicit operator Nullable<T>(DummyBox box)
    {
        if (box == null)
        {
            return new Nullable<T>();
        }

        // This should never be possible, as a DummyBox cannot be instantiated.
        throw new InvalidCastException();
    }
}

Однако это не объясняет, что произошло со мной следующим: если свойство HasValue false для любого значения Nullable<T>, тогда это значение будет помечено как null:

int? i = new int?();
object x = i; // Now x is null.

Кроме того, если HasValue - true, тогда значение будет помещаться как T, а не T?:

int? i = 5;
object x = i; // Now x is a boxed int, NOT a boxed Nullable<int>.

Но это, по-видимому, подразумевает, что существует неявное преобразование с Nullable<T> в object:

public static implicit operator object(Nullable<T> value);

Это явно не так, поскольку object является базовым классом для всех типов, а пользовательские неявные преобразования в/из базовых типов являются незаконными (также они должны быть).

Кажется, что object x = i; должен помещать i как любой другой тип значения, так что x.GetType() даст тот же результат, что и typeof(int?) (а не бросает a NullReferenceException).

Итак, я вырыл бит и, конечно же, выяснилось, что это поведение специфично для типа Nullable<T>, специально определенного как в спецификациях С#, так и в VB.NET, и не воспроизводимо в любом пользовательском struct (С#) или Structure (VB.NET).

Вот почему я все еще смущен.

Это конкретное поведение в боксе и распаковке представляется невозможным для реализации вручную. Он работает только потому, что и С#, и VB.NET обеспечивают специальное обращение к типу Nullable<T>.

  • Не теоретически возможно, что может существовать другой язык на основе CLI, где Nullable<T> не было предоставлено это специальное лечение? И не будет ли тип Nullable<T> проявлять различное поведение на разных языках?

  • Как С# и VB.NET достигают такого поведения? Поддерживается ли это CLR? (То есть, CLR позволяет типу каким-то образом "переопределять" способ, которым он помещен в коробку, даже если сами С# и VB.NET запрещают его?)

  • Возможно ли (в С# или VB.NET) вставить a Nullable<T> в качестве object?

4b9b3361

Ответ 1

Происходит две вещи:

1) Компилятор рассматривает "null" не как нулевую ссылку, а как нулевое значение... нулевое значение для любого типа, в котором ему нужно преобразовать. В случае Nullable<T> это просто значение, которое имеет значение False для поля/свойства HasValue. Поэтому, если у вас есть переменная типа int?, вполне возможно, что значение этой переменной будет null - вам просто нужно изменить свое понимание того, что null означает немного.

2) Бокс-типа с нулевым значением получает специальное лечение самой CLR. Это актуально в вашем втором примере:

    int? i = new int?();
    object x = i;

компилятор будет указывать любое значение типа NULL по-разному на значения, отличные от значения NULL. Если значение не равно null, результат будет таким же, как в боксе, то же самое значение, что и значение типа с нулевым значением - так что int? со значением 5 будет вставлен так же, как int со значением 5 - "недействительность" теряется. Однако нулевое значение типа с нулевым значением помещается в поле только нулевой ссылке, а не создает объект вообще.

Это было введено в конце цикла CLR v2 по просьбе сообщества.

Это означает, что нет такой вещи, как "значение с нулевым значением в коробке".

Ответ 2

Вы правильно поняли: Nullable<T> получает специальную обработку от компилятора, как в VB, так и в С#. Поэтому:

  • Да. Компилятор языка должен иметь специальный случай Nullable<T>.
  • Использование рефакторов компилятора Nullable<T>. Операторы - это просто синтаксический сахар.
  • Не то, чтобы я знал.