Различные ответы обсуждают важность обнуляемых типов в целом. Существует дополнительный ответ для nullable boolean, в частности. Это трудно понять в C #, но очень легко понять, если вы посмотрите на нулевую логику в любой базе данных.
Допустим, вы отслеживаете список или таблицу Shipments
. У Shipment
есть DeliveryDate
, но вы, конечно, не знаете эту информацию до тех пор, пока не будет произведена отгрузка, и, вероятно, по крайней мере, через несколько дней после ее фактической доставки, когда ИБП наконец получит уведомление. вы. Поэтому, конечно, DeliveryDate
- это Nullable<DateTime>
(или DateTime?
).
Вы хотите получить список всех отправлений, которые были доставлены за последнюю неделю. Итак, вы пишете это:
var deliveredThisWeek = shipments.Where(s =>
s.DeliveryDate >= DateTime.Today.AddDays(-7));
Должны ли быть включены грузы с датой доставки null
? (Ответ, конечно, нет .)
ОК, так что же с этим:
var deliveredBeforeThisWeek = shipments.Where(s =>
s.DeliveryDate < DateTime.Today.AddDays(-7));
Должны ли грузы с null
датой поставки быть включены в эти результаты? Ответ до сих пор нет .
Так что теперь у вас любопытная ситуация. Вы можете подумать, что между этими двумя запросами вы получите все грузы в системе. A | !A
всегда true
, верно? Не тогда, когда вы имеете дело с нулями.
Даже этот не дает вам всех результатов:
var deliveredAnytime = shipments.Where(s =>
(s.DeliveryDate >= DateTime.Today.AddDays(-7)) ||
(s.DeliveryDate < DateTime.Today.AddDays(-7)));
Так как это возможно?
Чтобы точно представить эту логику, вам необходимо условие, которое не является истинным или ложным. И опять же, C # - плохой пример, потому что не реализует логику так, как вы этого ожидаете. Но в SQLese это имеет смысл, потому что:
[DeliveryDate >= BeginningOfWeek] = NULL
[DeliveryDate < BeginningOfWeek] = NULL
Очевидно, NULL OR NULL
по-прежнему NULL
, а не TRUE
. Таким образом, вы можете правильно сказать, что посылка не была доставлена до начала недели, и не были доставлены после. Или, точнее, мы не знаем когда оно было доставлено, поэтому мы не можем с уверенностью сказать, что оно соответствует ни одному из этих условий.
Но C # не такой последовательный. В C #, если DeliveryDate
равно нулю, тогда:
(s.DeliveryDate >= beginningOfWeek) == false
(s.DeliveryDate < endOfWeek) == false
Что дает вам правильный ответ на наш запрос выше, так что вы можете испытать искушение сказать, что обычная логическая логика достаточно хороша, за исключением того, что она все испортила:
var deliveredThisWeek = shipments.Where(s =>
!(s.DeliveryDate < DateTime.Today.AddDays(-7));
Ага ... теперь он возвращает нам null
даты доставки! Это не правильно! И прежде чем кто-то скажет "@ Aaronaught, о чем ты, конечно, это правильно! Эти грузы НЕ были доставлены до прошлой недели, поэтому условие должно охватывать их!" , остановись и подумай об этом второй.
NULL
на самом деле не означает, что они не были доставлены. NULL
означает, что мы не знаем , когда они были доставлены. Вполне возможно, что клерк получит подтверждение завтра и заполнит DeliveryDate
, как две недели назад, аннулируя данные, которые мы только что собрали. Должно быть , а не быть нулевыми экземплярами, возвращающимися из этого запроса, и все же они есть. Если вы напишете тот же запрос в SQL, эти результаты будут исключены.
Итак, почему вас должно волновать Nullable<bool>
, когда C #, по-видимому, нет? Таким образом, вы можете избежать попадания в эту ловушку в своем собственном коде:
public static bool? IsThisWeek(DateTime? dt)
{
return (dt != null) ? (bool?)(dt.Value > DateTime.Today.AddDays(-7)) : null;
}
var deliveredBeforeThisWeek = shipments.Where(s =>
(!IsThisWeek(s.DeliveryDate) == true));
// Or, the equivalent:
var alsoDeliveredBeforeThisWeek = shipments.Where(s =>
(IsThisWeek(s.DeliveryDate) == false));
Это немного неловко, но правильно. Мы написали запрос, который более или менее правильно сообщает о своем намерении, и (bool?)null
не равно true
или false
, поэтому мы получаем правильные результаты в обоих случаях.
Если вам когда-либо понадобится оценить состояние, при котором ответ может быть «Я не знаю» - используйте bool?
(AKA Nullable<bool>
) в качестве результата .
Таким образом, вызывающий абонент может решить, как обрабатывать ответ «Я не знаю», а не просто выбирать значение по умолчанию. Все остальное означает, что ваш класс лжет .