简体   繁体   中英

What is the use of Nullable<bool> type?

a bool variable could hold true or false, while bool? could be null as well.

Why would we need a third value for bool ? If it is not true , what ever it is, it is == false

Can you suggest a scenario where I would fancy a bool? instead.

Thanks

Something can be true, false or undefined for many reasons. How would one answer "Is your third child a girl?" if one only has two children? Both true and false are incorrect. Null would be appropriate as saying that the comparison doesn't apply.

Various answers discuss the importance of nullable types in general. There is an additional answer for the nullable boolean, specifically. It's hard to understand in C#, but very easy to understand if you look at null-valued logic in any database.

Let's say you're tracking a list or table of Shipments . A Shipment has a DeliveryDate , but of course you don't know this information until long after the shipment has been made, and probably at least a few days after the shipment was actually delivered, when UPS finally gets around to notifying you. So of course the DeliveryDate is a Nullable<DateTime> (or DateTime? ).

You want to retrieve a list of all shipments that were delivered during the last week. So you write this:

var deliveredThisWeek = shipments.Where(s => 
    s.DeliveryDate >= DateTime.Today.AddDays(-7));

Should shipments with a null delivery date be included? (The answer is, of course, no .)

OK, so what about this:

var deliveredBeforeThisWeek = shipments.Where(s =>
    s.DeliveryDate < DateTime.Today.AddDays(-7));

Should shipments with a null delivery date be included in these results? The answer is still no .

So now you have a curious situation. You might think that between the two of these queries, you would receive all shipments in the system. A | !A A | !A is always true , right? Not when you're dealing with nulls.

Even this one fails to get you all the results:

var deliveredAnytime = shipments.Where(s =>
    (s.DeliveryDate >= DateTime.Today.AddDays(-7)) ||
    (s.DeliveryDate < DateTime.Today.AddDays(-7)));

So how is this possible?

In order to accurately represent this logic, you need a condition that is not true or false. And again, C# is kind of a bad example here because it doesn't implement the logic the way you'd really expect it to. But in SQLese, it makes sense, because:

[DeliveryDate >= BeginningOfWeek] = NULL
[DeliveryDate <  BeginningOfWeek] = NULL

Clearly, NULL OR NULL is still NULL , not TRUE . That is how you can correctly say that a shipment was not delivered before the beginning of the week, and was not delivered after. Or, more accurately, we don't know when it was delivered, so we can't safely say that it does match either of those conditions.

But C# is not so consistent. In C#, if the DeliveryDate is null, then:

(s.DeliveryDate >= beginningOfWeek) == false
(s.DeliveryDate < endOfWeek) == false

Which gets you the right answer for our query above, so you might be tempted to say that regular boolean logic is good enough, except that this one messes it all up:

var deliveredThisWeek = shipments.Where(s =>
    !(s.DeliveryDate < DateTime.Today.AddDays(-7));

Aha... now it's giving us back null delivery dates! This isn't right! And before someone says "@Aaronaught, what are you on about, of course it's right! Those shipments were NOT delivered before last week, so the condition should cover them!" , stop and think about it for a second.

The NULL doesn't actually mean that they weren't delivered. The NULL means that we don't know when they were delivered. It's possible that a clerk will pick up the confirmation tomorrow, and fill in the DeliveryDate as two weeks ago, invalidating the data we just picked up. There should not be null instances coming back from that query, and yet there are. If you wrote the same query in SQL, those results would be excluded.

So, why should you care about Nullable<bool> when C# apparently doesn't? So you can avoid falling into this trap in your own code:

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));

This is a bit awkward, but correct. We've written a query that more or less communicates its intent properly, and (bool?)null does not equate to true or false , so we get the right results in both cases.

If you ever need to evaluate a condition where the answer might be "I don't know" - use a bool? (AKA Nullable<bool> ) as the result .

That way, the caller can decide how to handle an "I don't know" response instead of you simply choosing a default. Anything else means your class is lying .

a null value means "no value" or "unknown value". In this case, it's not true nor false, but undefined .

See: http://en.wikipedia.org/wiki/Many-valued_logic

It's good for when something hasn't been answered yet - think of a questionnaire. You have a list of y/n questions and only some have been answered. You wouldn't want to post true or false to the database table because the user hasn't answered the question yet.

除了true或false之外,它可能是未知的。在数据库中,NULL通常表示未知或没有值

Lazy programming!

public bool MyProp
{
    get { return (myProp = myProp ?? GetPropValue()).Value; }
}
private bool? myProp;

a null value means

  • I don't know.
  • Insufficient data at this time
  • The state of the cat before the box is opened
  • I haven't made up my mind yet
  • Profit

a null value means Mu

I used it in a Filter Value. Basically I have a bool field in the database, but I wanted three states: Only return rows where the value is true, only return rows where the value is false, do not filter and return all rows.

Without a nullable bool I would have to use either an enum or a second bool to determine "Filter by this field yes/no" which would add bloat.

可以为空可以表示如果你的程序可能需要知道的话,bool没有被设置或初始化

Well I could see it being used as a "Not determined yet" kind of thing, i use bools all the time and sometimes just two values isnt enough!! for instance:

bool? bl;

bl = true; //Yes

bl = false; //No

bl = null; // Not determined, so do nothing

in my opinion its just the third value to a bool.

If you're about to use a Nullable bool, I would consider using an Enum or other data structure instead. It may help you avoid mistakes when the meaning of NULL is not known.

Recently, I mishandled a Nullable bool. I won't go into the details of the mistake, but lets just say it was bad enough to require a full rollback. I began to wonder how practical it is to use in most applications. (In part in an attempt to hold on to my broken pride as a result of the stupid mistake 🙂 )

Then, I came across this post and Lee's accepted answer.

This seems to make sense, but the more time I spend with it, the answer becomes FALSE in practice instead of NULL. NULL is just too fuzzy.

Before answering this question one would have to assign a very specific meaning to NULL. Does NULL mean there was an error in the calculation? What if there is an error in the calculation before it is determined that there are only two children? (I would also argue that if there are indeed only two children, this could be handled before the question was posed to the program.)

Because we do not know what NULL means and because it is most definitely not TRUE (because, how could it be?) the BEST answer is FALSE.

Also, if this question indeed returns NULL, we've introduced a new definition to the domain. What is the scope of this definition?

So it would appear that TRUE or FALSE states represent a certainty regarding an outcome. NULL can be conceived of in at least three different states. For example, the statement “Thomas went to the bar last night.”

TRUE – Yes, Thomas went to the bar. (likeliest answer if you knew Thomas) FALSE – No, Thomas did not go to the bar. NULL – Huh? Knowable – Something went wrong in the first calculation. Eg you asked Thomas the question but sneezed while he replied , rendering you deaf momentarily. Simply asking him again will get you the answer.

Unknowable – The bar burned down and Thomas skipped town. There is no realistic way of getting the answer. (Please don't poke holes in this, but I know you will)

Not applicable – See the example above regarding the three children, but, bars and Thomases instead.

What does NULL mean here? Determining what NULL means must be performed on a case by case basis. This is why I believe it is often better to use an Enum or other structure as it more gracefully exposes the intention behind a non-binary return value.

You'd want to use that to cover the situation of "what if the user doesn't specify neither true nor false?"

It's just a way to cover all possible outcomes.

Let's say that an order has been placed but not shipped yet. What is the value of Order.ShippedDate ?

The old-fashioned way to handle this was to use a magical value like DateTime.MinValue . There were other ways to solve this problem (for example a wrapper type or an additional bool flag indicating that the order has not shipped yet). None of these is very satisfactory. What is needed is a uniform way to handle the problem of a missing or unknown value.

So the modern approach is to allow Order.ShippedDate to take on a value that semantically captures that the order has not shipped yet. That's the role that nullable types play. Semantically you should think of an instance of Nullable<T> with HasValue being false as representing "missing" or "unknown."

Further, databases have long allowed a column to allows null . For example, you could have an integer-valued column that allows null. How do you interact with such a column from code if you don't allow nullable int s? Again, you could use the approaches mentioned above but it is so much nicer when the language has a built-in facility for semantically capturing the situation that we are trying to model.

Here is another use case. Think of a hierarchical data structure that has a boolean property. Setting such property on the parent will apply to all of its children, unless those are set explicitly. Think Read only permissions for files and folders in Windows (which DO have a tri-state checkbox).

Parent1(readonly: true, evaluates true)
  |-Parent2(readonly: null, evaluates true)
    |-Child1(readonly: false, evaluates false)
    |-Child2(readonly: null, evaluates true)
 |-Parent3(readonly: false, evaluates false)
    |-Child1(readonly: false, evaluates false)
    |-Child2(readonly: null, evaluates false)

I think the primary motivation (at least it was presented like this :-)) for adding nullable types in C# 2 were databases. In database, any data type can have the NULL value and when mapping these to C#, this is a problem. Nullable types allow you to deal with this quite elegantly - you can use int? , bool? and others. They are used for example in LINQ to SQL.

What does the third value mean for booleans, that of course depends on the application. I guess that typically, it will mean that the value/field is not available.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM