How to null coalesce for Boolean condition? - c#

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?

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?.

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.

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

more syntactic sugar, the null-coalescing operator. C# 8.0 or later
if (Foo?.Any() ?? false) { }

Related

Implicit nullable cast inside if condition

I recently found some code with nullable if condition like this:
bool? nullableVariable;
//some lines of code involving nullableVariable
if (nullableVariable == true)
{
//some other lines of code
}
at first sight I thought it as potentially exception prone. My opinion was based on the fact that:
bool? nullableVariable = null;
bool variable = (bool)nullableVariable
would raise error System.InvalidOperationException: 'Nullable object must have a value.' and I expected that inside the if (nullableVariable == true) statement a cast of left operand from bool? to bool would be performed (my impression confirmed by the fact that moving cursor on == operator results in Visual Studio hint bool bool.operator == (bool left, bool right)).
Given that, I've been surprised seeing that:
bool? nullableVariable = null;
if (nullableVariable == true)
{
//some other lines of code
}
would not raise exception (skipping the code inside brackets).
So the question is: what is happening inside that if statement? maybe a hidden try-catch cast of left operand from bool? to bool with catch setting the result to false? or maybe right operand cast from bool to bool?? or something else?
See lifted operators in Nullable value types
I short, they are overloaded for comparisons like ==:
For the equality operator ==, if both operands are null, the result is
true, if only one of the operands is null, the result is false;
otherwise, the contained values of operands are compared.
So by comparing with true or false you can avoid handling the case that the nullable is null which makes the code more readable like in your example:
if (nullableVariable == true)
{
//some other lines of code
}
So whenever you compare(== or !=) a bool? with a bool the result is not a bool?(or null) but a bool with the rules described in the link above. That's why you can use it in the if.
As mentioned by others, the equality operators are lifted to the nullable equivalents.
There does seem to be slight differences in the way it is compiled. I'm not entirely sure why, but it may be to do with lack of compiler knowledge whether an operator is side-effecting for complex structs, whereas types like int and bool it knows what's going on.
Where A and B are nullables then,
A == B
This is compiled to the following for basic types like int:
(A.GetValueOrDefault() == B.GetValueOrDefault()) && (A.HasValue == B.HasValue)
And to this for more complex types:
(A.HasValue == B.HasValue) ? !A.HasValue || (A.GetValueOrDefault() == B.GetValueOrDefault())

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

I want to check if one of two strings contains a partial string.
Knowing that firstString or secondString can be null, I tried the following:
string firstString= getValue();
string secondString= getValue();
if (firstString?.Contains("1") || secondString?.Contains("1"))
{
//logic
}
I am getting a compile time error mentioning that:
Operator '||' cannot be applied to operands of type 'bool?' and
'bool?'
Seems like result of String?.Contains("1") is nullable boolean.
This error actually does make sense to me because running time might face:
{null||true, null||false, null||null, etc..}
What is the best way to override this error? can I avoid writing two nested ifs?
It depends how you want to evaluate a null value: as true or false. I am used to false.
If so, you can use the null-coalescing operator:
(firstString?.Contains("1") ?? false) || (secondString?.Contains("1") ?? false)
It will be a quite naive way of doing, but you can avoid nesting of if condition, by checking the string for empty or null with "&&" operator, as sample condition below.
if ((!string.IsNullOrEmpty(firstString) && firstString.Contains("1")) || (!string.IsNullOrEmpty(secondString) && secondString.Contains("1")))
{
//logic
}

Using Elvis operator in combination with string.Equals

I wonder why the following code works in C# 6.0:
(In this example data is a random class containing val as a public string)
if ("x".Equals(data.val?.ToLower()) { }
But the following line isn't:
if (data.val?.ToLower().Equals("x")) { }
Visual Studio shows me the following error:
Cannot implicitly convert type 'bool?' to 'bool'. An explicit conversion exists (are you missing a cast?)
if ("x".Equals(data.val?.ToLower()) { }
Will eventually return a boolean because the of Equals call but this:
if (data.val?.ToLower().Equals("x")) { }
when the expression is evaluated it will return a System.Nullable<bool> which is different than a bool (former is a struct that can be assigned the value null while the latter can only be true or false) the the if expects. Also, in C# a null value doesn't evaluate to false (according to the C# specification).
I don't have c# 6.0 to test but this should work
if (data.val?.ToLower().Equals("x") ?? false) { }
The first statement is evaluating the return value of Equals whereas the second statement evaluates to bool? which could be null.
Further to the other answers I think it's useful if you write the equivalent code out long hand so you can see what's going on.
The first line is equivalent to:
string temp = data.val != null ? data.val.ToLower() : null;
if ("x".Equals(temp)) { }
which is perfectly valid and obvious.
The second statement is equivalent to:
bool? temp = data.val != null ? data.val.ToLower().Equals("x") : null;
if (temp) { } //error
This is perhaps less obvious but the statement data.val?.ToLower().Equals("x") will return null if data.val is null otherwise it will return the result of data.val.ToLower().Equals("x"). Because the statement can return null or bool the type of the entire statement is bool? but you can't use a bool? in an if directly.
As others have pointed out, converting the bool? to a bool will fix your issue. One way of doing that is to use GetValueOrDefault():
if (data.val?.ToLower().Equals("x").GetValueOrDefault()) { }

Rewrite HasValue to the ?? Operators

Is it safe to rewrite the following code:
bool b = foo.bar.HasValue ? foo.bar.Value : false;
to
bool b = foo.bar.Value ?? false;
where bar is the nullable type bool?
The easiest fix there is
bool b = foo.bar.GetValueOrDefault();
which is also actually cheaper than .Value as it omits the has-value check. It will default to default(T) , which is indeed false here (it just returns the value of the underlying T field, without any checks at all).
If you need a different default to default(T), then:
var value = yourNullable.GetValueOrDefault(yourPreferredValue);
What you want is:
bool b = foo.bar ?? false;
This is (surprisingly) safe and an intended use for the null-coalescing operator.
The ?? operator is called the null-coalescing operator and is used to define a default value for a nullable value types as well as reference types. It returns the left-hand operand if it is not null; otherwise it returns the right operand.
Source: http://msdn.microsoft.com/en-us/library/ms173224.aspx
In the case of Nullable<T>, it is functionally equivalent to Nullable<T>.GetValueOrDefault(T defaultValue).
The code:
bool b = foo.bar.Value ?? false;
Will cause a compiler-error, because you cannot apply the operator to value types, and Nullable<T>.Value always returns a value-type (or throws an exception when there is no value).
No - this is not safe.
The line:
bool b = foo.bar.Value ?? false;
will throw an InvalidOperationException if foo.bar has no value.
Instead use
var b = foo.bar ?? false;
Update - I just learned about .GetValueOrDefault(); from the other answers - that looks like a very good suggestion to use!
Update 2 - #ProgrammingHero's answer is also correct (+1 added!) - the line:
bool b = foo.bar.Value ?? false
actually won't compile - because of Error 50 Operator '??' cannot be applied to operands of type 'bool' and 'bool'
Nope.
The Value property returns a value if
one is assigned, otherwise a
System.InvalidOperationException is
thrown.
From: http://msdn.microsoft.com/en-us/library/1t3y8s4s%28v=vs.80%29.aspx
So if hasValue is false then you will get an exception thrown in your second one when you try to run it.
ffoo.bar ?? false would be more safer to use
bar.Value ?? false
has compilation error, because left operand of ?? operator should be of reference or nullable type.
Maybe you have a look at this article in Stackoverflow too - it is an elegant null checking in the style obj.IfNotNull(lambdaExpression) - returning the object you want if obj is not null otherwise just null (but without throwing an exception).
I used it with the Entity Framework if you're accessing a referenced entity, e.g.
var str=Entity1.Entity2.IfNotNull(x=>x.EntityDescription) ?? string.Empty
which returns EntityDescription contained in Entity2 which is referenced by Entity1 - or an empty string if any object Entity1 or Entity2 is null. Without IfNotNull you would get a long and ugly expression.
The extension method IfNotNull is defined there as follows:
public static TOut IfNotNull<TIn, TOut>(this TIn v, Func<TIn, TOut> f)
where TIn : class
where TOut: class
{
if (v == null) return null; else return f(v);
}
Update:
If you update the code to C# version 6.0 (.NET Framework 4.6 - but seems to support older frameworks 4.x too), there is a new operator available which makes this task easy: The "elvis" operator ?..
It works as follows:
var str=(Entity1.Entity2?.EntityDescription) ?? string.Empty
In this case, if Entity2 is null, evaluation stops and (...) becomes null - after which the ?? string.empty part replaces null by string.Empty. In other words, it works the same way as .IfNotNull(...) would.
foo.bar.Value represents the non-nullable value when there is one, and throws an InvalidOperationException if there’s no real value.

Type result with conditional operator in C#

I am trying to use the conditional operator, but I am getting hung up on the type it thinks the result should be.
Below is an example that I have contrived to show the issue I am having:
class Program
{
public static void OutputDateTime(DateTime? datetime)
{
Console.WriteLine(datetime);
}
public static bool IsDateTimeHappy(DateTime datetime)
{
if (DateTime.Compare(datetime, DateTime.Parse("1/1")) == 0)
return true;
return false;
}
static void Main(string[] args)
{
DateTime myDateTime = DateTime.Now;
OutputDateTime(IsDateTimeHappy(myDateTime) ? null : myDateTime);
Console.ReadLine(); ^
} |
} |
// This line has the compile issue ---------------+
On the line indicated above, I get the following compile error:
Type of conditional expression cannot be determined because there is no implicit conversion between '< null >' and 'System.DateTime'
I am confused because the parameter is a nullable type (DateTime?). Why does it need to convert at all? If it is null then use that, if it is a date time then use that.
I was under the impression that:
condition ? first_expression : second_expression;
was the same as:
if (condition)
first_expression;
else
second_expression;
Clearly this is not the case. What is the reasoning behind this?
(NOTE: I know that if I make "myDateTime" a nullable DateTime then it will work. But why does it need it?
As I stated earlier this is a contrived example. In my real example "myDateTime" is a data mapped value that cannot be made nullable.)
The compiler does not infer the type of the result of the conditional operator from the usage of the result, but from the types of its arguments. The compiler fails when it sees this expression because it cannot deduce the type of the result:
IsDateTimeHappy(myDateTime) ? null : myDateTime;
Since null and DateTime are not compatible, you need to tell the compiler what the type should be. A cast should do the trick:
DateTime? x = IsDateTimeHappy(myDateTime) ? (DateTime?)null : myDateTime;
OutputDateTime(x);
Now the compiler will have no problems. You can also write the above on one line if you prefer (but I would probably not do this):
OutputDateTime(IsDateTimeHappy(myDateTime) ? (DateTime?)null : myDateTime);
Eric Lippert has a good answer that is also relevant here and goes into more details about how the compiler determines types.
The reason is the ternary operator expects both the operands to be of the same type. The whole operator get worked out BEFORE it is assigned to a result (in this case passed into a function), so the compiler can't know what the result type is.
IsDateTimeHappy(myDateTime) ? null : myDateTime
In the above case there is no conversion path between null and DateTime. As soon as you cast one of them to DateTime?, the compiler can convert the other one:
IsDateTimeHappy(myDateTime) ? (DateTime?)null : myDateTime
//OR
IsDateTimeHappy(myDateTime) ? null : (DateTime?)myDateTime
The fist line of code above works because the compiler can convert DateTime to DateTime? via an implicit conversion operator:
//In Nullable<T>
public static implicit operator T?(T value);
The second line works because null can be assigned to DateTime? since the latter is a reference type.
The implicit conversion is not allowed by the return statement. If you had
if (condition)
return first_expression;
else
return second_expression;
Then you'd be comparing apples to apples. And you'd have no problems - as you stated.
In your case, you're allocated so much space on the stack for a DateTime - which is a non-nullable value type. So you're making a statement that doesn't make any sense to the compiler. If you say, I'm going to pass you an A or a B, then the A and the B need to be the same thing. In your case, the B can never be an A.
What the compiler is saying is:
If IsDateTimeHappy(myDateTime) is false, then I need to return a value of type DateTime equal to myDateTime. If it is true, then I need to return a value equal to null, but you haven't told me what type it should be!
That's why Mark's answer is the solution. After you provide a cast telling the compiler what type of value will be returned if the condition is true, it can check whether the true and false return values can be converted to (or are of) the same type.
Cheers Mark! ;-)
Instead of null use default(DateTime?) and then both sides of the ternary will have compatible types.

Categories

Resources