Trying to understand ?. (null-conditional) operator in C# - c#

I have this very simple example:
class Program
{
class A
{
public bool B;
}
static void Main()
{
System.Collections.ArrayList list = null;
if (list?.Count > 0)
{
System.Console.WriteLine("Contains elements");
}
A a = null;
if (a?.B)
{
System.Console.WriteLine("Is initialized");
}
}
}
The line if (list?.Count > 0) compiles perfectly which means that if list is null, the expression Count > 0 becomes false by default.
However, the line if (a?.B) throws a compiler error saying I can't implicitly convert bool? to bool.
Why is one different from the other?

list?.Count > 0: Here you compare an int? to an int, yielding a bool, since lifted comparison operators return a bool, not a bool?.
a?.B: Here, you have a bool?. if, however, requires a bool.

In your first case (list?.Count) the operator returns an int? - a nullable int.
The > operator is defined for nullable integers so that if the int? has no value (is null), the comparison will return false.
In your second example (a?.B) a bool? is returned (because if a is null, neither true nor false but null is returned). And bool? cannot be used in an if statement as the if statement requires a (non-nullable) bool.
You can change that statement to:
if (a?.B ?? false)
to make it work again. So the null-coalescing operator (??) returns false when the null-conditional operator (?.) returned null.
Or (as TheLethalCoder suggested):
if (a?.B == true)

Related

Checking if a List is Null before getting Count results in -> Cannot implicitly convert type 'int?' to 'int

I'm trying to find the number of items present in a list.In order to prevent the null exception.I'm using the ? operator to check whether myBenchmarkMappings is null before getting its count.
int benchmarkAccountCount =portfolioWrapper.myBenchmarkMappings?.Count;
But this results in Cannot implicitly convert type 'int?' to 'int'. exception
What am i doing wrong ?
This is because the ? operator will return the value or null. And you can't assign null to int.
You have two options. First is to mark the int as nullable.
But in this case latter you will need to check if the int is null.
int? benchmarkAccountCount = portfolioWrapper.myBenchmarkMappings?.Count;
if (benchmarkAccountCount == null)
{
// null handling
}
The second option, which I think is better, you can user the null-coalescing operator ?? and give the benchmarkAccountCount a default value in case the list is null.
int benchmarkAccountCount = portfolioWrapper.myBenchmarkMappings?.Count ?? 0;
When .myBenchmarkMappings is null, then .myBenchmarkMappings?.Count is also null. This means this code could possibly assign a null to an int, but an int can't store a null. Change the type to int? if you want to accommodate an int or null.
int? benchmarkAccountCount = portfolioWrapper.myBenchmarkMappings?.Count;
You can try to use ?: operator to check if list is null:
int benchmarkAccountCount =portfolioWrapper.myBenchmarkMappings!=null ? portfolioWrapper.myBenchmarkMappings.Count:0;

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

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

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

!= null vs. =null

See below code first please.
using System;
using System.Collections.Generic;
namespace ConsoleApplication1
{
class Program
{
public struct MyStruct
{
public List<MyStructItem> Items;
}
public struct MyStructItem
{
public string Value;
}
static void Main(string[] args)
{
List<MyStruct> myList = new List<MyStruct>();
myList.Add(new MyStruct());
//(!) it haven't comipled.
if (myList[0].Items = null){Console.WriteLine("null!");}
//(!) but it have compiled.
if (myList[0].Items != null) { Console.WriteLine("not null!"); }
}
}
}
What is difference between !=null and =null in that case?
thanks.
You are using the assignment operator = instead of the equality operator ==.
Try:
//(!) it haven't comipled.
if (myList[0].Items == null){Console.WriteLine("null!");}
//(!) but it have compiled.
if (myList[0].Items != null) { Console.WriteLine("not null!"); }
The difference is one compiles and one doesn't :-)
C# Operators:
http://msdn.microsoft.com/en-us/library/6a71f45d(v=vs.80).aspx
= null is assignment. You should use == null
You assign the null value to myList[0].Items and tries to use it as bool in if statement. That is why code can not be compiled.
For example, this code compiles successfully:
bool b;
if (b = true)
{
...
}
Because you set true value to b and then check it in if statement.
=null you set the value to null
!= null you check if it is different from null
If you want to compare if it is equal to null, use == null instead of = null.
'=' is for assignment, not for comparing. Use '=='
if (myList[0].Items == null){Console.WriteLine("null!");}
First off, myList[0].Items = null will set the object to null. You probably mean myList[0].Items == null
And regarding the difference, == checked if something is equal. != checks if something is not equal.
For predefined value types, the equality operator (==) returns true if
the values of its operands are equal, false otherwise. For reference
types other than string, == returns true if its two operands refer to
the same object. For the string type, == compares the values of the
strings.
And
The assignment operator (=) stores the value of its right-hand operand
in the storage location, property, or indexer denoted by its left-hand
operand and returns the value as its result. The operands must be of
the same type (or the right-hand operand must be implicitly
convertible to the type of the left-hand operand).
Reference: http://msdn.microsoft.com/en-us/library/6a71f45d(v=vs.71).aspx
= is the assignment operator, you should use the equality operator ==
= null is an assignment. == null is a condition.
if (myList[0].Items != null)
is a negative comparison. It checks if myList[0].Items is not equal to null.
if (myList[0].Items = null)
is an assignment. It would normally assign null to myList[0].Items and return true (in languages like C++), however, in C#, this won't compile (because of this common error).

Categories

Resources