Implicit nullable cast inside if condition - c#

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

Related

"ulong == ulong?" evaluated as "ulong == ulong" works correctly

If we use the == operator between an expression of ulong and an expression of ulong?, then the operator overload bool ulong(ulong left, ulong right) appears to be used.
In other words, the operator considers both expressions non-null.
In this sample program, equal correctly becomes false, with no exceptions.
void Main()
{
var temp = new Temp(0);
object temp2 = null;
var equal = temp.Id == (temp2 as Temp)?.Id; // False :) but how?
}
public class Temp
{
public ulong Id {get;}
public Temp(ulong id)
{
this.Id = id;
}
}
How can this not throw? There is no conversion from a ulong? with value null to a ulong. (ulong)(ulong?)null throws: "Nullable object must have a value."
Does this return the correct value for every possible value of ulong?, including null? If so, how? The type ulong? has one more possible value than ulong, so there should be one set of two values that map to the same ulong value, which would introduce one false positive "true" result.
In theory, I could imagine null being coalesced to default(ulong), but then the result in my example above would be true, which would be an incorrect answer. And as we can see, the compiler does not make that mistake - it answers correctly.
From MSDN:
Lifted operators permit predefined and user-defined operators that operate on non-nullable value types to also be used with nullable forms of those types. Lifted operators are constructed from predefined and user-defined operators that meet certain requirements, as described in the following:
...
For the equality operators
== !=
a lifted form of an operator exists if the operand types are both non-nullable value types and if the result type is bool. The lifted form is constructed by adding a single ? modifier to each operand type. The lifted operator considers two null values equal, and a null value unequal to any non-null value. If both operands are non-null, the lifted operator unwraps the operands and applies the underlying operator to produce the bool result.
You're not using the operator:
bool ==(ulong left, ulong right)
You're using the lifted operator:
bool ==(ulong? left, ulong? right)
This operator takes two ulong? parameters, and returns true if both are null, or if both are non-null and have the same value.
You're probably looking at Visual Studio, which does show you something confusing in this case:
Don't be confused by this -- as #mjwills pointed out in the comments, this is a known issue.
If you write this:
public bool M(ulong a, ulong? b) {
return a == b;
}
Then the compiler produces the following code:
public bool M(ulong a, ulong? b)
{
ulong? num = b;
return (a == num.GetValueOrDefault()) & num.HasValue;
}
num.GetValueOrDefault() returns 0 if b is null, otherwise the value of b. So M returns true if and only if b is not null, and has the same value as a.
SharpLab
If we use the == operator between an expression of ulong and an
expression of ulong?, then the operator overload bool ulong(ulong
left, ulong right) is used.
A large part of the issue is that Visual Studio's intellisense incorrectly shows ulong's == operator being used (if you hover over ==).
This is a bug, as per https://github.com/dotnet/roslyn/issues/21494 . It is not actually using that operator.
Since all of your other questions are based on that faulty premise, realising the existence of that bug makes the other issues largely disappear.

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
}

How to null coalesce for Boolean condition?

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) { }

Odd C# Ternary Operator Behavior

So I came across some very baffling behavior of the native ternary operator in C# today. The ternary operator is sending the wrong data type to the method I'm calling. The basic premise is I want to convert the decimal value to an int if decimalEntry == false so it will get stored in a database as an int Here is the code:
decimal? _repGroupResult = 85.00
int? intValue = null;
bool decimalEntry = false;
if (decimalEntry == false)
{
intValue = (int?) _repGroupResult;
}
Console.WriteLine("Sending to ResultAdd via ternary operator");
RepGBParent.ResultAdd(this.RepInfo.ResultID, decimalEntry ? _repGroupResult : intValue);
Console.WriteLine("Sending to ResultAdd via if statement");
// All other tests - just add the rep group result
if (decimalEntry)
{
RepGBParent.ResultAdd(this.RepInfo.ResultID, _repGroupResult);
}
else
{
RepGBParent.ResultAdd(this.RepInfo.ResultID, intValue);
}
The method I'm calling ResultAdd is here:
public void ResultAdd(int pResultID, object pResultValue)
{
if (pResultValue == null) { return; }
Console.WriteLine(this.TestInfo.TestNum + ": " + pResultValue.GetType());
....
}
The ternary operator receives a decimal while the if statement sends an int. As shown by the output code below:
I consider myself a programmer of reasonable talent and this really threw me back today. I played around with it for 2-3 hours and figured out the best way to post it here so I'm clear of the issue I'm having.
Please avoid "why are you even doing it that way" type responses. I simply want to know why there is a difference here between the ternary operator and the if statement.
The only other post that I found that was closely related was this one but it didn't quite match up:
Bizarre ternary operator behavior in debugger on x64 platform
The ternary operator is - well - an operator which is only a special kind of method. And as any other method, it can only have one single return type.
What you try to do is to use the operator to return a decimal? or an int? depending on a condition. That is not possible.
What happens is that the compiler knows that there is an implicit conversion from int? to decimal?, but not the other way round. So it infers the return type of the operator as decimal? and implicitly converts your intValue into a decimal?.
The ternary expression returns a single type, not a type conditional on the result of the evaluation.
Your int is promoted to decimal in order to fullful that requirement.
If the conversion could not be applied, you would get a complier error.
Either the type of first_expression and second_expression must be the same, or an implicit conversion must exist from one type to the other.
https://msdn.microsoft.com/en-us/library/ty67wk28.aspx

C# Null propagating operator / Conditional access expression & if blocks

The Null propagating operator / Conditional access expression coming in c#-6.0 looks like quite a handy feature. But I'm curious if it will help solve the problem of checking if a child member is not null and then calling a Boolean method on said child member inside an if block:
public class Container<int>{
IEnumerable<int> Objects {get;set;}
}
public Container BuildContainer()
{
var c = new Container();
if (/* Some Random Condition */)
c.Objects = new List<int>{1,2,4};
}
public void Test()
{
var c = BuildContainer();
//Old way
if ( null != c && null != c.Objects && c.Objects.Any())
Console.Write("Container has items!");
//C# 6 way?
if (c?.Object?.Any())
Console.Write("Container has items!");
}
Will c?.Object?.Any() compile? If the propagating operator short circuits (I assume that's the right term) to null then you have if (null), which isn't valid.
Will the C# team address this concern or am I missing the intended use case for the null propagating operator?
It won't work this way. You can just skip the explanation and see the code below :)
As you know ?. operator will return null if a child member is null. But what happens if we try to get a non-nullable member, like the Any() method, that returns bool? The answer is that the compiler will "wrap" a return value in Nullable<>. For example, Object?.Any() will give us bool? (which is Nullable<bool>), not bool.
The only thing that doesn't let us use this expression in the if statement is that it can't be implicitly casted to bool. But you can do comparison explicitly, I prefer comparing to true like this:
if (c?.Object?.Any() == true)
Console.Write("Container has items!");
Thanks to #DaveSexton there's another way:
if (c?.Object?.Any() ?? false)
Console.Write("Container has items!");
But as for me, comparison to true seems more natural :)
Null-conditional operator would return null or the value at the end of expression. For value types It will return result in Nullable<T>, so in your case it would be Nullabe<bool>. If we look at the example in the document for Upcoming Features in C# (specified here), it has an example:
int? first = customers?[0].Orders.Count();
In the above example, instead of int, Nullable<int> would be returned. For bool it will return Nullable<bool>.
If you try the following code in Visual Studio "14" CTP:
Nullable<bool> ifExist = c?.Objects?.Any();
The result of the above line would be a Nullable<bool>/bool?. Later you can do the comparison like:
Using null-coalescing operator ??
if (c?.Object?.Any() ?? false)
Using Nullable<T>.GetValueOrDefault Method
if ((c?.Objects?.Any()).GetValueOrDefault())
Using comparison against true
if (c?.Objects?.Any() == true)
var x = c?.Objects?.Any() is going to give you a nullable boolean, and as others have said, this means you can use an equality operator like this
x == true
or you can use null coalescing like this to have your result not be nullable
var x = c?.Objects?.Any() ?? false
But, personally, I think that 3-state (nullable) booleans are code smell. Even if this one is practically invisible, its existence should encourage you to think about what you are actually trying to do and determine if a nullable boolean is actually the way to go. In this case, I think that what you really want to do is something like this -
var objects = c?.Objects ?? Enumerable.Empty<Object>();
if (objects.Any())
{
...
}
Put that in an extension method and it will be even more succinct -
public static bool IsNullOrEmpty<T>(this IEnumerable<T> collection)
{
return !(collection ?? Enumerable.Empty<T>()).Any()
}

Categories

Resources