В чем причина, по которой не все типы значений являются обнуляемыми?
Есть ли какое-либо наказание, так что вы должны установить их только как nullable, когда вам это действительно нужно?
Спасибо
Ответы
Ответ 1
Различные причины:
- История:
Nullable<T>
не существовало до .NET 2.0, и он не может сломать существующий код - особенно с различными правилами бокса для Nullable<T>
- Значение: если я хочу
int
, я бы не мог хотеть его обнулять... Я хочу, чтобы он был int
, и я не хочу, чтобы check-for/handle nulls
- Пробел: он добавляет дополнительные затраты для каждой структуры... в частности, представьте себе
byte[]
, и теперь рассмотрим, было ли значение byte
равно нулю - много дополнительных накладных расходов; * плюс это остановит вас делать blits и т.д. *
-
Производительность: Nullable<T>
добавляет множество дополнительных штрафов; в частности много скрытых вызовов .HasValue
и .Value
/.GetValueOrDefault()
; это проявляется, в частности, в "снятых операторах" - т.е. x + y
становится чем-то вроде ниже, которое складывается для жестких циклов и т.д.:
(x.HasValue && y.HasValue) ? (x.GetValueOrDefault() + y.GetValueOrDefault()) : null
Аналогично, x == y
должен проверить:
- если оба значения null = > true
- если один null = > false
- используйте
==
на GetValueOrDefault()
для каждого
лоты накладных расходов....
Ответ 2
Да, есть штраф. Структура Nullable<T>
содержит значение, а также логический флаг, который определяет нулевое значение.
Хотя логическое значение - это только один байт, структуры выравниваются с четными границами слов. В int
используется четыре байта памяти, но для int?
требуется восемь байтов.
Ответ 3
Какие типы значений фактически являются нулевыми? Я ничего не знаю.
EDIT: Если вы ссылаетесь на тип строки, то это не тип значения, а ссылочный тип.
Ответ 4
В дополнение к ответу Марка Гравелла, проверьте отображаемый код от Nullable <T> :
public struct Nullable<T> where T: struct
{
private bool hasValue;
internal T value;
public Nullable(T value)
{
this.value = value;
this.hasValue = true;
}
public bool HasValue
{
get
{
return this.hasValue;
}
}
public T Value
{
get
{
if (!this.HasValue)
{
ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_NoValue);
}
return this.value;
}
}
public T GetValueOrDefault()
{
return this.value;
}
public T GetValueOrDefault(T defaultValue)
{
if (!this.HasValue)
{
return defaultValue;
}
return this.value;
}
public override bool Equals(object other)
{
if (!this.HasValue)
{
return (other == null);
}
if (other == null)
{
return false;
}
return this.value.Equals(other);
}
public override int GetHashCode()
{
if (!this.HasValue)
{
return 0;
}
return this.value.GetHashCode();
}
public override string ToString()
{
if (!this.HasValue)
{
return "";
}
return this.value.ToString();
}
public static implicit operator T?(T value)
{
return new T?(value);
}
public static explicit operator T(T? value)
{
return value.Value;
}
}
Вы заметите существование логического HasValue
и того, как его используют свойства.
Ответ 5
Рассмотрим реализацию. Целое число в С# определяется как
public struct Int32 : IComparable, IFormattable, IConvertible, IComparable<int>, IEquatable<int>
Структура хранится непосредственно в стеке, в отличие от ссылок на объекты, которые являются указателями на память (сама ссылка находится в стеке, но выделение находится в стеке).
Ссылка NULL просто означает, что указатель в стеке не был инициализирован в папку в куче.