41

I have the following piece of code in my LINQ:

    where (tf.Shipped || tf.Ordered || tf.Processed)

Note that Shipped, Ordered and Processed are all nullable Boolean fields

I am getting the following message:

Operator || cannot be applied to operands of type 'bool?' and 'bool?'

Not sure how to resolve this as yes, they need to be nullable booleans and I need to use the OR (||).

recursive
  • 83,943
  • 34
  • 151
  • 241
Nate Pet
  • 44,246
  • 124
  • 269
  • 414
  • What's the point of a nullable Boolean? Serious question. – DoctorMick Jan 27 '12 at 17:05
  • 4
    @DoctorMick Tons of uses. Let's say I have a health assessment form that asks if the patient has had a prostate exam. That doesn't apply to women. It's not `true` or `false`. It's `null` - for N/A in this case. – Yuck Jan 27 '12 at 17:07
  • @DoctorMick or simply True/False/Unknown. – adelphus Jan 27 '12 at 17:08
  • 1
    What are you expecting? A result similar to that of comparisons with SQL's `NULL`? i.e. When your value is `null`, should it simply be taken as `false`? – Ry- Jan 27 '12 at 17:10
  • for the purposes of the 'if' statement null values need to either be treated as true or as false. Determine which you want to use and use the '??' operator or the .HasValue property as demonstrated in the answers. Personally, I like using the '??' operator. – Servy Jan 27 '12 at 17:14
  • 1
    @DoctorMick I remember before nullables we had a type in a code-base named `Troolean`. Serious, and useful. – Grant Thomas Jan 27 '12 at 17:22
  • Related: http://stackoverflow.com/questions/5204366/why-are-there-no-lifted-short-circuiting-operators-on-bool – CodesInChaos Jan 27 '12 at 17:24
  • Please specify what your expected behavior is. – CodesInChaos Jan 27 '12 at 17:25
  • @CodeInChaos - The expected behavior is obvious. He is trying to evaluate boolean values using a boolean operation. Since a boolean operation only expect two states, one can assume that a value that is not True must be False. I read the discussion you started, and in another language NULL is what basically amounts to an additional state. In C# a boolean value only has two states, and the default state, is **false.** – Security Hound Jan 27 '12 at 17:52
  • 1
    It's absolutely not obvious to me what he expects if all of them are `null`. – CodesInChaos Jan 27 '12 at 17:53
  • @CodeInChaos - In the context of the code, if all 3 values are null, then none of them are true. So the where statement would return the result where any or all of them are false. If I a am mistaken in this logic then correct me. If he wanted different results he would have used different code. – Security Hound Jan 27 '12 at 18:03
  • @Ramhound There are at least two possible interpretations of his code: "All of them are true" and "None of them is false". These interpretations give different extrapolations to tri-state-bools. – CodesInChaos Jan 27 '12 at 18:09
  • 7
    @Ramhound: "null" sometimes means "this thing has no truth value", as Yuck suggests. "null" can also mean "this thing has a truth value but I don't know what it is right now because that information hasn't been entered into the database yet". It is *perilous* to assume that a *missing* value is *necessarily false*. Did that order ship? The answer "we don't know" is not the same as the answer "no"! If it were, then there would be no need for nullables in the first place. – Eric Lippert Jan 27 '12 at 18:32
  • 1
    I'd use the enum and you should as well. – Lukasz Madon Jan 27 '12 at 18:33
  • "they need to be nullable booleans" : strange, and the root of this and a lot of future problems. – H H Jan 28 '12 at 23:39

6 Answers6

104

Take a step back and think about the problem. You want a collection of widgets where the widget was ordered, or the widget was shipped, or the widget was processed.

There are four possible states for your knowledge of "ordered":

  • this widget was ordered and I know that (true)
  • this widget was not ordered and I know that (false)
  • this widget was ordered but I don't know that (null)
  • this widget was not ordered but I don't know that (null)

There are four states but only three values possible values. Therefore if "ordered" is in the null state you do not know whether it should be included in the query results or not.

The compiler doesn't know that either.

There simply is not enough information available for the compiler to give you a query that has the semantics you want. The compiler is not going to make a guess and possibly give you bad results; the compiler is going to tell you that there's not enough information here and you need to do more work to make the query unambiguous.

What you have to do is say what to do in the case where you don't know the answer. The query "all the widgets that were ordered, shipped or processed" is impossible because some widgets we don't know whether they were ordered, shipped or processed, and so we don't know whether to include them or not. But the query "all the widgets that I know were ordered, or that I know were shipped, or that I know were processed" is a query that the compiler can make sense of:

where (tf.Shipped ?? false) || (tf.Ordered ?? false) || (tf.Processed ?? false)

That means "if I don't know whether it was shipped, etc, assume it was not".

You might instead want the query "all the widgets that definitely were, or might have been shipped, ordered or processed:

where (tf.Shipped ?? true) || (tf.Ordered ?? true) || (tf.Processed ?? true)

The compiler isn't going to guess which side you want to err on when there is insufficient information to give accurate results; the compiler might guess wrong and we're not in the business of making decisions on your behalf. You're going to have to make that decision.

Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
  • 11
    +1 This answer explains exactly what the problem is and how to solve it. It should be the accepted answer. – Remko Jansen Feb 01 '12 at 11:10
  • Dear scribes, please add this answer to the canon. – hemp Feb 02 '12 at 19:00
  • You can also use `where (tf.Shipped | tf.Ordered | tf.Processed) ?? false` or `where (tf.Shipped | tf.Ordered | tf.Processed) ?? true` if you prefer that. C# defines operator `|` specifically for nullable boolean operands, so it is not simply a lifted operator. – Jeppe Stig Nielsen Dec 16 '14 at 23:28
  • There's nothing for the compiler to guess. With `x||y||z`, I want to know whether either x, y or z is definitely true. The rules are the same as for the `|` operator: true if at least one is true, false if all of them are false, null otherwise. So if x returns true, `||` could safely short-circuit with no need for y and z to be evaluated, just like with ordinary non-nullable bools. So the reason why this isn't supported can't be "the compiler can't possibly know what you meant", it's probably development costs or some other (probably valid) reason. – relatively_random Feb 07 '23 at 08:12
  • @relatively_random: By any chance were you on the compiler team or design committee when I wrote this answer in 2012? – Eric Lippert Feb 08 '23 at 17:18
  • @relatively_random: I'll also note that the point of this answer is that the compiler cannot read your mind to know that *you*, specifically, mean "I want to know if this is definitely true" vs "I want to know if this is *maybe* true". The latter is a valid interpretation; the fact that it is not *your* personal interpretation is a fact about you. The question you should be considering is not "what happens if x is true" but rather, "what happens if x is null?" – Eric Lippert Feb 08 '23 at 17:26
  • I wasn't on the design team, of course, and I did say there's probably a valid reason why this wasn't included. Also, I very much appreciate all the insight into the language design process you provided over the years. My comment was just about how there's no reason why `||` _couldn't_ be consistently and logically defined since `|` already is. I realize that the resulting `bool?` isn't a valid condition for `if` or `where`, but that's not what the title or the error message in the OP are about. – relatively_random Feb 09 '23 at 07:57
  • @relatively_random: My point is that I was on the design team when I wrote this answer, so it's maybe a little presumptuous to say that my stated reasons are not "valid" and "can't possibly" be right. You're free to think they're bad reasons and that there is a design that you'd personally like, of course. – Eric Lippert Feb 09 '23 at 17:55
39

Try

 where (tf.Shipped == true || tf.Ordered  == true || tf.Processed == true )
18

You need to ensure the expression is never null. You could do this with the null-coalesce operator, ??:

where ((tf.Shipped ?? false) || (tf.Ordered ?? false) || (tf.Processed ?? false))
Ry-
  • 218,210
  • 55
  • 464
  • 476
adelphus
  • 10,116
  • 5
  • 36
  • 46
5
where ((tf.Shipped.HasValue && tf.Shipped.Value)
       || (tf.Ordered.HasValue && tf.Ordered.Value)
       || (tf.Processed.HasValue && tf.Processed.Value))
sll
  • 61,540
  • 22
  • 104
  • 156
  • 3
    Interesting solution, the runtime will not fail on null `.Value` because it will not check the right side of `&&` operator statement if `.HasValue` (left side) is `false`. – Tomislav Markovski Jan 27 '12 at 17:37
  • In order to explain TomislavMarkovski comment one has to realize that if all the values are null, then none of the values are true, and thus the OR of 3 False statements is False. Likewise if HasValue is False the AND statement is always False, the runtime knows this, which is the reason you are able to check the Value of a reference that has a Null value. – Security Hound Jan 27 '12 at 17:49
2

You can also use the GetValueOrDefault in your specific case.

where (tf.Shipped.GetValueOrDefault()
    || tf.Ordered.GetValueOrDefault()
    || tf.Processed.GetValueOrDefault() )
SandRock
  • 5,276
  • 3
  • 30
  • 49
1
where ((tf.Shipped.HasValue && tf.Shipped.Value) 
   || (tf.Ordered.HasValue && tf.Ordered.Value) 
   || (tf.Processed.HasValue && tf.Processed.Value)) 
Nate
  • 30,286
  • 23
  • 113
  • 184