Null[able] Hacks

Знаете ли вы, как работает nullNullable) в различных операциях и логических выражениях в C#? Некоторые моменты неочевидны.

Арифметика

Например, null пожирает остальные значения в арифметических операциях и превращает результат в null:

Где это может пригодиться

Не знаю, где это может пригодиться, но это довольно забавно. А если серьезно, то просто имейте это в виду.

Сравнение с числами

Если сравнивать null с числами, то он одновременно не меньше и не больше их:

Где это может пригодиться

Например, у нас есть строка, но мы не знаем, null она или нет, а нам надо проверить ее длину. В голову может прийти такой неэлегантный вариант:

Или даже так:

IsNullOrWhiteSpace, как и IsNullOrEmpty — хорошая (отличная!) практика, но в данном случае это излишне, поскольку мы сразу же за этим проверяем длину строки (что нам придется делать в любом случае). Поэтому можно написать проще:

А теперь вспомним наш хак и сократим это еще больше:

Если str == null, выражение выдаст false, поскольку str?.Length == null. Если же строка не null, будет произведена обычная проверка ее длины.

Пример:

Сравнение с булевскими величинами

С булевскими значениями все без сюрпризов. null не равен как true, так и false:

Где это может пригодиться

null у нас опять сущность из другого измерения, которая ни с чем не совместима. Но эта особенность позволяет немного элегантнее проверять значения переменных типа bool?.

Мы довольно часто встречаемся с булевскими полями в сущностях БД, которые могут быть пустыми. Бывают и такие параметры функций.

Например, у нас есть такой код:

Нам нужно проверить, особенный это проект или нет, за это отвечает поле project.IsSpecial, тип которого bool?. У него может быть три значения: null, false и true. Первые два соответствуют значению ложь, и только третье означает истина, что типично для булевских полей БД, которые могут быть null.

Таким образом, чтобы узнать, что поле имеет значение истина, нам нужно убедиться, что оно не пустое и имеет значение true. Если бы у нас было простое значение bool, мы бы просто написали:

Но оно у нас Nullable, поэтому все печально, и нам приходится писать такие монструозные конструкции, как:

Или:

Что немногим лучше.

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

Мало того, что код получается длинным и малочитабельным, так мы еще имеем и повторение кода, которое тоже чревато последствиями. Одним из вариантов будет вынесение campaign.Project во временную переменную (и я рекомендую так делать для увеличения читабельности кода и избавления от его дублирования), но есть вариант и получше.

Нам на помощь приходит вышеописанный волшебный хак.

Вуаля! Элегантно и просто, не правда ли?

Как вариант:

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

Для вложенной структуры все будет выглядеть примерно так же:

Что позволяет не проверять на null все части пути к финальному полю.

Что же нам делать, если нам нужно проверить значение bool? на false? Да примерно то же самое:

Обратите внимание, что вот так писать нельзя, потому что значение null мы тоже считаем ложью:

Сортировка

Мы выяснили, что null не равен ни true, ни false. Но что произойдет, если отсортировать массив значений типа bool??

Проверить это очень просто:

Результат:

Получается, что null меньше любого другого значения, причем это не зависит от типа. С числами и строками будет то же самое.