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.
Ответ 3
вы можете сделать .HasValue, чтобы проверить, равна ли переменная
вы можете сделать
myvar = nullablevar ?? defaultvalue; //will set defaultvalue if nullablevar is null
и более, это не ново для С# 4
читайте это для получения дополнительной информации
Ответ 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.