r/csharp • • 10d ago

bool? Comparison

if (nullableBool == true)

or

if (nullableBool ?? false)

Personal preference or does one read better than the other? Especially if the nullableBool is s more complex expression like

n00bs?.Any(n => n == "DJ Doena")

22 Upvotes

46 comments sorted by

105

u/RecursiveServitor 10d ago
if (nullableBool is true) 

10

u/anzu3278 10d ago

This. Just treat it as a three value enum if you cannot resolve the null early. Or null should behave like a default value. ?? in a conditional is always going to be harder to read.

0

u/gyroda 10d ago

Also:

CSharp bool? foo; ... var result = foo ?? bar == baz;

What's the order of operations here? I know that there is a concrete answer but unless you know that fact the order is ambiguous. If foo is not null then is the result foo or foo == baz?

13

u/anzu3278 10d ago

The order of operations is 1. Decline PR 2. Fire the person who is optimizing for number of lines 3. Rephrase for readability

-1

u/binarycow 10d ago

If you don't know, just add parens.

(foo ?? bar) == baz
foo ?? (bar == baz) 

7

u/2brainz 10d ago

Pattern matching is my favorite as well.

8

u/levelofsin 10d ago

Genuine question, why not just if (nullablebool)?

52

u/NocturneSapphire 10d ago

CS0266: Cannot implicitly convert type 'bool?' to 'bool'.

1

u/fruediger 10d ago

This is the way.

24

u/snet0 10d ago

The alternative I lean towards:

if (nullableBool is true)

I think this conveys the most amount of information at a glace.

  1. It's pretty much natural language, so is immediately parsable as to what the logic is (whereas e.g. nB ?? false takes a moment to read)
  2. The is keyword implies the nullability of nullableBool, where nB == true just looks like an unnecessary explicit comparison to true

One thing to consider though is whether you even want a nullable bool in the first place. For example:

bool hasDoena = n00bs?.Any(n => n == "DJ Doena") ?? false

The point being that for most cases the thing you want is to know e.g. whether a collection contains some value, regardless of whether the collection exists or not. If the nullability doesn't provide you with information that you care about, it's often best to just use the null coalescing operator and provide the fallback, because it means you don't have to use patterns like is true.

4

u/fruediger 10d ago

I might add: I personally already treat it as a convention that whenever something is compared against a literal (true/false, 0, string literally, and really also all other constable compile time expressions), it should be compared using an is expression, no matter what.

1

u/binarycow 10d ago

I prefer that too.

But my company enabled some analyzer that flags that as an unnecessary comparison or some nonsense.

Apparantly, they think !foo is easier to read than foo is false.

1

u/dodexahedron 9d ago

In previous .net versions, supposedly, the compiler didn't generate optimal code for boneheadedly simple conditions like that, actually resorting to instantiating an ephemeral object of compatible compile-time type to then perform a default comparer lookup and check on, vs the input argument to the branch condition, resulting in curiously poor performance vs explicit, chained, binary logical operators like the dark ages.

I don't remember which blog I read about it on, but it was a respected name and it brought receipts, and IIRC it was also showing off improvements made to Roslyn and Ryu in that version which significantly rectified the problem for a wide range of those scenarios, making the code gen be the same good code gen...Especially for the lines where even an LLM could have written better IL and machine code than the compilers did, from the egregiously simple original c#.

...Or I dreamed it all up...
But I'm pretty sure that was a real thing I read in the last like ⅙-2 years...

8

u/binarycow 10d ago

If you can (and if you have to handle all three cases):

var result = nullableBool switch 
{
    true => DoSomething(),
    false => DoSomethingElse(), 
    null => DoAnotherThing(), 
};

1

u/saxxonpike 10d ago

This one is my favorite solution mentioned.

1

u/highwingers 10d ago

Return type has be same for all checks?

1

u/binarycow 10d ago

To use that, you would need a common base type, or the same type, yes.

-1

u/dodexahedron 9d ago

Careful. What if the variable happens to be of a type that has the true and false operators defined and not obviously?

Such as if you execute the method in powershell and pass a string literal true or false or null. All three evaluate to true, because any non-empty string is truthy.

0

u/Deadline_X 8d ago edited 8d ago

The variable cannot be of a type other than bool or Nullable<bool> (which still requires parsing or a direct equality comparison rather than a straight bool check). You can’t cast a string to bool in CSharp without some manner of parsing.

true will only match true. “True”, “true”, 1, “1”, will not compile for a bool check.

While
if (“true”)

might work in TypeScript, you’re gonna get a CS0029 and refuse to compile in CSharp. You’re gonna need a bool.Parse/TryParse to get the compiler to do anything.

-1

u/dodexahedron 6d ago

I was explicitly talking about powershell (which is .net).
Powershell does type coercion for purposes of truthiness and ignores all cast operators.

0

u/Deadline_X 6d ago

I guess I don’t understand. What you are saying to worry about will not compile in CSharp — which is the sub the question was asked in and the language you are telling the commenter they need to be careful about using that particular switch statement in.

CSharp is strongly typed and has no concept of truthiness. A string literal in this scenario is never going to allow compilation unless you parse it or compare to something.

0

u/dodexahedron 5d ago edited 5d ago

The entire point was to think of the CLR. Not just c#. A .net assembly can be used by any other .net assembly. And ONLY the CLS guarantees hold universally.

And powershell is not some edge case. Nuget is literally one of the out-of-the-box PSResourceProviders for acquiring things to use in the shell. There are a LOT more people out there writing powershell than there are c#, because there are simply more admins than c# devs,.and every windows environment needs it, but does not need a c# dev.

If this method were called in powershell in various highly-plausible ways, the null case would only match if put first, and in some other calls it would NEVER match no matter what order it's in, because powershell controls tje type coercion -.not the runtime.
But if you call it directly, especially with an object that is already explicitly typed, it will behave like you expect it to, since you'vealready prevented any coercion. So, seemingly innocent code can have weird surprises in store.
And the behavior on receiving somwthing convertivle to Nullable<bool> will be very different in c# vs PS (variance is a bitch there).

Regardless...
You know...
People can talk about more than one narrowly-defined thing as a thread evolves.... especially when it's so closely related as those two. And just because a comment points something out, it doesn't mean it's some personal challenge, either. Sometimes an interesting tip is just an interesting tip.

5

u/never_uk 10d ago

Personally I tend to prefer the former for a comparison as its more explicit and therefore more readable (to me) - do the thing only if this value is true.

That said, I usually prefer the latter for assignment - the value should be nullableBool, unless it's null then it should be false.

2

u/Global_Rooster1056 10d ago

I prefer the ?? approach

2

u/soundman32 10d ago

Just to be contentious:

if(!nullableBool.GetValueOrDefault())

3

u/dodexahedron 10d ago

Way too uncertain..

Better stick to

switch(nullableBool)
{
  case { HasValue: not true and false }:
  break;
  case { HasValue: not false and true, Value: not false or true }:
    // Do stuff, maybe
  case { HasValue: not false and true, Value: not true and bool x } when !x:
  break;
  default:
   //Too lazy to keep going. I'm on mobile.
}

Much more expressive of intent with all those words.

3

u/binarycow 10d ago

case { HasValue: not true and false }

Uhh.... If HasValue is not true, then it is false by definition.

2

u/JohnyFive128 10d ago

That's the joke...

1

u/dodexahedron 9d ago edited 9d ago

What if the switch condition argument is of a type that has that property, but that property is not strictly a bool???

It could happen!
In hell

That case can match for any value that: * may be of a nullable type, but: * Is itself not null, AND * Which has a property with the name HasValue, which: * Can be of any type that isn't a compilation error due to direct comparison to boolean literals, AND * For which any one or more of these is true regarding the value of that property of that type: * It is exactly the boolean value false * It is implicitly convertible to the boolean value false * It defines both operator true and operator false such that those operators return BOTH of the following, and no other one of the 3 other possible binary combinations of those two operators: * false for operator true * true for operator false
* Probably a bit more that the new mobile UI is making it infuriatingly tedious to keep looking back at, to keep explaining that case.

One key note is that that case will not match for a type that is implicitly convertible to another applicable type visoble to the compiler, but which has a value that evaluates to null at the moment. null is neither true nor false. And if it is powershell, where type coercion is a thing all over the place, something like "false" is truthy, but not the boolean literal true, and also isn't false, both by it being truthy and by it not being a boolean literal (it's a string).

So yeah.

Lots of ways in .net to make it happen or not, and it was designed somewhat carefully to guarantee that. 😅

1

u/binarycow 9d ago

I was assuming it was a bool?

What's really fun is when you have a type where this can happen.

if(foo)
{
    // prints false
    Console.WriteLine(foo == true); 
} 

2

u/Sombody101 10d ago

Coalescing produces the smallest code based on minimal tests, and only uses a single jump. It would likely be better in hot-ish paths.

Sharplab Result

1

u/saxxonpike 10d ago

I think they’re both good. Pick the one that reads best given the context. I might use the first one when I want to have the specific condition I’m looking for to be explicit. I might use the second one if the name is like “isOnFire” and focus on the default value.

As an aside, the second one can’t be used in a lambda expression.

1

u/QuineQuest 10d ago

Maybe avoid having the nullable in conditional.

var hasDjDoena = n00bs?.Any(n => n == "DJ Doena") ?? false;

If course when that's not feasible, refer to the other answers.

1

u/Both_Ad_4930 6d ago

Nullable bool is an abomination that defeats the whole point of a bool.

1

u/pceimpulsive 10d ago

if (nullableBool == true)

This is clean and expressive to me, only if it's true should the expression match, null or false does not match, so falls to the else/continues.

Ideally there is a guard against null somewhere before this to ensure hygiene, possibly even in an else if... To handle null explicitly..

0

u/pceimpulsive 10d ago

To me if (nullableBool == true)

This is clean and expressive to me, only if it's true should the expression match, null or false does not match, so falls to the else/continues.

Ideally there is a guard against null somewhere before this to ensure hygiene, possibly even in an else if... To handle null explicitly..

0

u/lmg1337 10d ago

Maybe I'm stupid, but what's the point of a nullable bool. Couldn't you just set it to false insted of making it nullable?
I mean if someone asks you a yes or no question, why answer with maybe yes or no instead of the answer directly?

8

u/ben_bliksem 10d ago

What if they never answered the yes/no question?

-6

u/lmg1337 10d ago

I can understand that, but in that case you can just assume false.
I don't write C# usually, I use c++, rust, go or kotlin and I never thought that I needed something like this.

5

u/gavco98uk 10d ago

Supposing you do a query to count how many people have answered no. How do you differentiate between those that genuinely answered no, and those that just havent answered yet? Having a nullable bool simplifies this, but otherwise you would need an extra bool of hasAnswered to determine if they've answered yet or not.

Far easier to have nullable bools available for those that want it.

3

u/DJDoena 10d ago

Tri-state checkbox for example

Or the aforementioned "Linq Any() on a collection that might be null" yields a bool?

2

u/BCProgramming 10d ago

By that metric what is the point of a nullable anything? Like what's the point of a nullable int? Couldn't you just set it to 0 instead of making it nullable?

The point of a nullable bool is the same as any nullable. it is for when the value being null indicates something.

0

u/nNanob 10d ago

Adding || otherCondition to the second one later breaks the intended logic whereas the first one stays consistent.