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

Бокс/Unboxing Nullable Types - Почему эта реализация?

Извлечь из CLR через С# в бокс /Unboxing типы значений...

В боксе: если нулевым экземпляром не является null, CLR берет значение из экземпляра с возможностью nullable и помещает его в поле. Другими словами, Nullable < Int32 > со значением 5 помещается в boxed-Int32 со значением 5.

В Unboxing: Unboxing - это просто акт получения ссылки на незапакованную часть объекта в штучной упаковке. Проблема состоит в том, что тип с коротким значением не может быть просто распакован в нулевую версию этого типа значений, потому что в бокс-значении нет поля boolean hasValue. Таким образом, при распаковке типа значения в нулевую версию CLR должен выделять Nullable <T> , инициализируйте поле hasValue до true и установите для поля значение то же значение, которое находится в коробке тип значения. Это влияет на производительность вашего приложения (выделение памяти при распаковке).

Почему команда CLR столкнулась с такими трудностями для типов Nullable? Почему это не просто вставляется в Nullable < Int32 > в первую очередь?

4b9b3361

Ответ 1

Я помню, что это поведение было своего рода последним изменением. В ранних версиях .NET 2.0 Nullable<T> был "нормальным" типом значения. Бокс a null оцененный int? превратил его в коробку int? с булевым флагом. Я думаю, что причина, по которой они решили выбрать текущий подход, - это последовательность. Скажи:

int? test = null;
object obj = test;
if (test != null)
   Console.WriteLine("test is not null");
if (obj != null)
   Console.WriteLine("obj is not null"); 

В предыдущем подходе (в поле null → boxed Nullable<T>) вы не получили бы "test is not null", но вы получите "object is not null", который является странным.

Кроме того, если они ввели значение с нулевым значением в значение boxed-Nullable<T>:

int? val = 42;
object obj = val;

if (obj != null) {
   // Our object is not null, so intuitively it an `int` value:
   int x = (int)obj; // ...but this would have failed. 
}

Кроме того, я считаю, что текущее поведение имеет смысл для сценариев, таких как значения NULL для базы данных (думаю, SQL-CLR...)


Разъяснение:

Весь смысл предоставления типов с нулевым значением заключается в том, чтобы упростить работу с переменными, которые не имеют значимого значения. Они не хотели предоставлять два разных, несвязанных типа. int? должен вести себя более или менее как простой int. Именно поэтому С# предоставляет снятые операторы.

Итак, при распаковке типа значения в нулевую версию CLR должен выделить объект Nullable<T>, инициализировать поле hasValue равным true и установить для поля значений то же значение, которое находится в типе значений в штучной упаковке. Это влияет на производительность вашего приложения (выделение памяти при распаковке).

Это неверно. CLR должна будет выделять память на стек, чтобы сохранить переменную независимо от того, является она или нет. Там нет проблемы с производительностью для выделения пространства для дополнительной логической переменной.

Ответ 2

Я думаю, имеет смысл придать пустое значение нулевой ссылке. Имея значение в коробке: "Я знаю, что я был бы Int32, если бы у меня было значение, но я не" кажется мне неинтуитивным ". Лучше перейти от версии типа значения" не значение "(значение с HasValue как false) к версии ссылочного типа" не значение" (нулевая ссылка).

Я считаю, что это изменение было внесено в обратную связь сообщества, кстати.

Это также позволяет интересное использование as даже для типов значений:

object mightBeADouble = GetMyValue();

double? unboxed = mightBeADouble as double?;
if (unboxed != null)
{
    ...
}

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

object mightBeADouble = GetMyValue();

if (mightBeADouble is double)
{
    double unboxed = (double) mightBeADouble;
    ...
}

(Он также может работать лучше, поскольку проверяется только один тип времени выполнения).

Ответ 3

То, что вы получаете благодаря этому поведению, заключается в том, что в коробке реализованы все интерфейсы, поддерживаемые базовым типом. (Цель состоит в том, чтобы сделать Nullable<int> таким же, как int для всех практических целей.) Бокс в boxed-Nullable<int> вместо boxed-int предотвратит это поведение.

На странице MSDN

double? d = 44.4;
  object iBoxed = d;
  // Access IConvertible interface implemented by double.
  IConvertible ic = (IConvertible)iBoxed;
  int i = ic.ToInt32(null);
  string str = ic.ToString();

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

float f = 1.5f;
object boxed_float = f;
int int_value = (int) boxed_float; // will blow up. Cannot unbox a float to an int, you *must* unbox to a float first.

float? nullableFloat = 1.4f;
boxed_float = nullableFloat;
float fValue = (float) boxed_float;  // can unbox a float? to a float    Console.WriteLine(fValue);

Здесь вам не нужно знать, была ли исходная версия версией int или Nullable. (+ вы получаете некоторый перфоманс, сохраняете место хранения hasValue boolean, а также в помещенном в коробку объекте)

Ответ 4

Я предполагаю, что это в основном то, что он делает. В приведенном описании содержится ваше предложение (например, бокс в Nullable<T>).

Кроме того, он устанавливает поле hasValue после бокса.

Ответ 5

Я бы предположил, что причина поведения связана с поведением Object.Equals, в первую очередь тем фактом, что если первый объект является нулевым, а второй - нет, Object.Equals возвращает false, а не вызывает метод Equals на втором объекте.

Если Object.Equals вызвал бы метод Equals для второго объекта в случае, когда первый объект был нулевым, а второй не был, то объект, который был нулевым Nullable <T> мог бы вернуть True по сравнению с null. Лично я считаю, что правильным средством было бы сделать свойство HasValue Nullable <T> не имеют ничего общего с концепцией нулевой ссылки. Что касается накладных расходов, связанных с хранением булевого флага в куче, можно было бы предусмотреть, что для каждого типа Nullable <T> была бы статическая пустая версия с коротким номером, а затем предусмотреть, что распаковка статической пустой пустой копии приведет к пустой Nullable <T> , и unboxing любого другого экземпляра даст заполненный.