16

I'm trying to safely check if an IList<> is not empty.

var Foo = Bar.GimmeIListT(); // Returns an IList<SomeObject>
if (Foo?.Any())
    // Do cool stuff with items in Foo

But there is an error with the condition:

Cannot implicitly convert 'bool?' to 'bool'. An explicit conversion exists (are you missing a cast?)

So it seems the condition evaluates to a nullable bool, so I try

if (Foo?.Any().Value())

But this is no good either:

'bool' does not contain a definition for 'Value' and no extension .... blah blah blah

So in the first instance it complains that it is a nullable bool but in the second it complains that it isn't.

As another avenue I try:

if (Foo?.Any() == true)

This works - but it shouldn't because this uses an implicit conversion which the first message said it didn't want!

What is going on? What is the correct way to do this?

Toby
  • 9,696
  • 16
  • 68
  • 132
  • if (Foo?.Count > 0) { } – Andzej Maciusovic Jul 18 '17 at 10:16
  • 1
    @AndzejMaciusovic: that's a bad way. If `Foo` is actually a query that takes hours to execute completely you will miss the `Any` badly. If you want to know if there is at least one, don't count all. – Tim Schmelter Jul 18 '17 at 10:18
  • 1
    @TimSchmelter In my example I'm using .Count not Count() .Count is already calculated field on IList, in fact it is a bit faster to use .Count instead of .Any(). on List. – Andzej Maciusovic Jul 19 '17 at 07:03
  • 2
    @AndzejMaciusovic: true, but people will understand this as a good way in general and overlook the missing paranthesis(In VB.NET they're optional anyway). – Tim Schmelter Jul 19 '17 at 07:38

4 Answers4

19

You can compare with the bool? if you use ==, that's indeed the best/simplest approach:

if (Foo?.Any() == true) ...

as to the why it's not allowed in an if but with ==, Jon Skeet can explain it much better:

There's no implicit conversion from Nullable<bool> to bool. There is an implicit conversion from bool to Nullable<bool> and that's what happens (in language terms) to each of the bool constants in the first version. The bool operator==(Nullable<bool>, Nullable<bool>) operator is then applied. (This isn't quite the same as other lifted operators - the result is just bool, not Nullable<bool>).

In other words, the expression fred == false is of type bool, whereas the expression fred is of type Nullable<bool> hence you can't use it as the "if" expression.

So the if allows only bool and you have a bool?, but the == operator converts the bool to a bool? and you can compare two bool?.

Tim Schmelter
  • 450,073
  • 74
  • 686
  • 939
3

Edit:

It seems like the cause of the bool? is the Foo?.Any() itself. If you do not want to compare it with true, I would suggest you to have temporary variable:

bool? any = Foo?.Any(); 
if (any.Value) ...

Alternatively, if the object is a class, you could use FirstOrDefault() != null as checking condition. It won't take time because it will only get the first object:

if (Foot?.FirstOrDefault() != null)...

I will go with the temporary variable or the Foo?.Any() == true option though.

Original:

Note: It is to my surprise too that if (a?.Any()) cannot be followed by .Value() or .Value(!).

I think what you need is Value (property) without () (method):

if (Foo?.Any()?.Value) ... 

bool? has .Value (property) which is a bool.

Ian
  • 30,182
  • 19
  • 69
  • 107
3

Any() returns bool but Foo?.Any() will return bool?

So Foo?.Any().Value won't compile since Any() returns a bool that doesn't have a member Value.

If Foo is null, Any() won't be executed because the statement will return null without interpreting the part behind the ?. operator.

But if you put Foo?.Any() in paranthesis, you are able to work with the result of type bool? and check it via Value or GetValueOrDefault():

(Foo?.Any()).GetValueOrDefault()
Stephan Bauer
  • 9,120
  • 5
  • 36
  • 58
2

more syntactic sugar, the null-coalescing operator. C# 8.0 or later

if (Foo?.Any() ?? false) { }
Rashid Ali
  • 587
  • 3
  • 13