Gotchas при использовании Nullable <T> в С# 4

Я только начал писать на компоненте, где обнаружил, что было бы полезно объявить некоторые свойства nullable, вместо того чтобы позволить им прибегнуть к значениям по умолчанию. Однако я понял, что раньше никогда не использовал синтаксис non-nullable-type? или тип Nullable<T>, поэтому, вероятно, есть некоторые gotchas, которые скоро выпрыгнут и укусят меня. Так что...

  • Каковы самые большие ошибки при использовании Nullable<T> и сокращенного синтаксиса ??

  • Как мне обойти их?

  • Каковы самые большие преимущества/новые возможности, которые доступны мне, когда я начну их использовать?

Ответ 1

Общим методом получения является назначение переменной с нулевым значением с условным выражением следующим образом:

bool useDefault = true;
int defaultValue = 50;
int? y = useDefault ? defaultValue : null; 

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

Type of conditional expression cannot be determined because there is no
implicit conversion between 'int' and '<null>'

Решение: добавьте приведение к одному или обоим возможным результатам:

int? y = useDefault ? defaultValue : (int?)null; 

Менее распространено: обычно можно с уверенностью предположить, что для целых чисел a <= 5 и !(a > 5) эквивалентны. Это предположение неверно для нулевых целых чисел.

int? x = null;
Console.WriteLine(x <= 5);
Console.WriteLine(!(x > 5));

Результат:

False
True

Решение. Обработайте нулевой случай отдельно.


Вот еще одно небольшое изменение выше:

int? y = null;
int? z = null;
Console.WriteLine(y == z);
Console.WriteLine(y <= z);

Вывод:

True
False

So y равно z, но не меньше или равно z.

Решение. Опять же, обработка нулевого случая отдельно может избежать неожиданностей.

Ответ 2

То, что люди часто удивляются, заключается в том, что нет такой вещи, как тип с нулевым значением в коробке. Если вы скажете:

int? x = 123;
int? y = null;
int z = 456;
object xx = x;
object yy = y;
object zz = z;

вы можете подумать, что поскольку zz содержит вложенный в ящик int, то xx и yy содержат вложенные в нуль ints. Они не. xx содержит коробчатый int. yy имеет значение null.

Ответ 4

Nullable<T> - это особый тип значения. Это может помочь, если вы поймете, как это работает. Есть несколько тонких вещей об этом, которые не сразу очевидны. Я писал о здесь.

Фактические gotchas - не многие. Почти самый большой из них заключается в том, что явный бросок может вызывать InvalidOperationException (а не NullReferenceException). Компилятор должен вести вас к любым проблемам, которые могут возникнуть.

Ответ 5

Nullable<T> типы немного необычны; они (строго говоря) ни ценности, ни ссылочные типы, а что-то странное между ними. Бокс/распаковка и typeof, в частности, имеют специальные правила, поэтому результаты менее неожиданны.

Подробнее, я рекомендую книгу Jon Skeet С# в глубине. Если вы еще этого не владеете, вам нужно.:)

Ответ 6

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

То есть, нулевое значение не является "другим" - оно означает неизвестное или неисчислимое в это время или что, учитывая другие значения в этом объекте/записи, для него не имеет смысла быть значением. Ваша ситуация звучит так, как будто она действительна - пользовательские настройки могут быть нулевыми, а фактические значения будут

actualValue = userValue ?? defaultValue;

?? - это оператор нулевой коалесценции, - это эквивалентно следующему:

actualValue = userValue.HasValue ? userValue : defaultValue;

или

actualValue = (userValue != null) ? userValue : defaultValue;

Искушение, которое я вижу в людях чаще всего, использует bool? как флаг трех государств. Это не имеет смысла, потому что нет никакой третьей возможности в true/false/???. Другие, читающие ваш код, должны будут копаться в комментариях (в лучшем случае), чтобы узнать, что истинное предназначено для использования синего текста, false означает красный текст, а null - зеленый текст. Используйте перечисления для этого.

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

Ответ 7

В настоящее время я не использую 4.0, но в 2.0 у меня есть проблема, которая, как и большинство Generics, int? не может быть легко сериализована. Возможно, 4.0 умнее Reflection, но утилиты XML-сериализатора по умолчанию не могут его прочитать. Это довольно раздражает.

Ответ 8

Установите значение по умолчанию бессознательно, используя значение NULL. Я видел это несколько раз. Например.

int? localVar = 0;
// do something ..
if (localVar.HasValue)
   _myMemberValue = localVar.Value;

LocalVar никогда не является нулевым, потому что он был инициализирован значением, поэтому тест для HasValue является правдивым, а _myMemberValue может неправильно назначаться с неправильным значением.

- Изменено, чтобы добавить дополнительные комментарии ----

Забыл упомянуть. Одним из основных преимуществ, которые я видел, является использование поля для представления поля Nullable в базе данных. Это также генерируется автоматически, если вы используете Linq и EF. Но традиционно до Nullable это ручная работа и ошибка, склонная обрабатывать обновление поля и решая, когда устанавливать его со значением или значением null.