Как сделать код с nullable decimal и сложением более очевидным в C#
Что проверяют
Знаете ли вы, как работают поднятые (lifted) операторы для Nullable<T>, и замечаете ли, что null в деньгах это вопрос смысла, а не синтаксиса. Следом часто показывают строку вида a ?? 0 + b ?? 0 и просят сказать, что она вернёт.
Как отвечать
Начните с поведения по умолчанию: для decimal? a, b выражение a + b имеет тип decimal? и вернёт null, если хотя бы один операнд null. Код без явной обработки молча теряет всю сумму, поэтому «очевидность» тут в том, чтобы намерение было видно из строки.
Дальше варианты, каждый со своим смыслом:
(a ?? 0m) + (b ?? 0m):nullтрактуется как ноль. Скобки обязательны, а суффиксmпоказывает тип литерала явно;a.GetValueOrDefault() + b.GetValueOrDefault(): то же самое,default(decimal)равен нулю, выбор по вкусу команды;new[] { a, b, c }.Sum(): для последовательностиdecimal?методSumпропускаетnullи возвращает ноль, если значений нет вовсе. Удобно, когда слагаемых больше двух;- сохранить
null: если «неизвестно» не равно «ноль», оставьтеa + bи назовите метод так, чтобы это читалось, напримерTotalOrNull, или избавьтесь отnullраньше, на границе с данными; a.Value: бросаетInvalidOperationExceptionприnull, использовать только после проверкиHasValueили сопоставленияa is decimal x.
Сильный ответ заканчивается вопросом к требованиям: отсутствующая скидка это ноль, а отсутствующая цена скорее ошибка, и её лучше поймать, чем сложить с нулём. Иногда самый очевидный код получается, когда null вообще не доходит до арифметики.
Где спотыкаются
Пишут a ?? 0 + b ?? 0 без скобок. У ?? приоритет ниже, чем у +, выражение читается как a ?? ((0 + b) ?? 0), и при непустом a значение b просто теряется.
Путают поведение Enumerable.Sum и SQL: в C# сумма одних null даёт ноль, а SUM в базе вернёт NULL. Если часть расчёта идёт сырым SQL через Dapper, итог в отчёте и в коде может разойтись.
Прячут выбор между нулём и null в самописный помощник без внятного названия. Читатель снова открывает реализацию, и очевидности не прибавилось.