? (nullable) operator in C#
As others have said, "?" is just shorthand for changing it to Nullable<T>
. This is just another value type with a Boolean flag to say whether or not there's really a useful value, or whether it's the null value for the type. In other words, Nullable<T>
looks a bit like this:
public struct Nullable<T>
{
private readonly bool hasValue;
public bool HasValue { get { return hasValue; } }
private readonly T value;
public T value
{
get
{
if (!hasValue)
{
throw new InvalidOperationException();
}
return value;
}
}
public Nullable(T value)
{
this.value = value;
this.hasValue = true;
}
// Calling new Nullable<int>() or whatever will use the
// implicit initialization which leaves value as default(T)
// and hasValue as false.
}
Obviously in the real code there are more methods (like GetValueOrDefault()
) and conversion operators etc. The C# compiler adds lifted operators which effectively proxy to the original operators for T
.
At the risk of sounding like a broken record, this is still a value type. It doesn't involve boxing... and when you write:
int? x = null;
that's not a null reference - it's the null value of Nullable<int>
, i.e. the one where hasValue
is false
.
When a nullable type is boxed, the CLR has a feature whereby the value either gets boxed to a null reference, or a plain boxed T. So if you have code like this:
int? x = 5;
int y = 5;
object o1 = x;
object o2 = y;
The boxed values referred to by o1
and o2
are indistinguishable. You can't tell that one is the result of boxing a nullable type.
The += operator with nullable types in C#
Here is the difference between the two statements:
x += x ?? 1
x = (x + x) ?? 1
The second isn't what you were expecting.
Here's a breakdown of them both:
x += x ?? 1
x += null ?? 1
x += 1
x = x + 1
x = null + 1
x = null
x = x + x ?? 1
x = null + null ?? 1
x = null ?? 1
x = 1
Trying to understand ?. (null-conditional) operator in C#
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)
C# null or null-conditional operator in boolean expression
As Rofus and Jeroin correctly pointed out in comments the conversion between T to T? is always implicit when you do the comparison with "Null
" 7 becomes a "Nullable<int>
"
bool bar = foo?.Count > 7;
is similar to -
int? count = null;
int? k = 7; // Implicit conversion here!
var bar = count > k;
For your second question on the >
operator. Yes, while the Nullable<T>
struct does not define operators such as <
, >
, or even ==
, still the following code compiles and executes correctly which is similar to your case -
int? x = 3;
int? y = 10;
bool b = x > y;
This is because compiler lifts
the greater than operator from underlying value type like -
bool b = (x.HasValue && y.HasValue) ? (x.Value > y.Value) : false;
Operator Lifting
or Lifted Operators
means you can implicitly use T's operators on T?. Compiler has different set of rules on different set of operators on handling them on Nullable<T>
types.
Does the ?. operator do anything else apart from checking for null?
Because Nullable<T>
is implemented in C# in a way that makes instances of that struct appear as nullable types. When you have DateTime?
it's actually Nullable<DateTime>
, when you assign null
to that, you're setting HasValue
to false
behind the scenes, when you check for null
, you're checking for HasValue
, etc. The ?.
operator is just implemented in a way that it replaces the very same idioms that work for reference types also for nullable structs. Just like the rest of the language makes nullable structs similar to reference types (with regard to null
-ness).
Why does the == operator work for NullableT when == is not defined?
Given these two facts, it surprises me that this compiles
Given only those two facts, that is surprising.
Here's a third fact: in C#, most operators are 'lifted to nullable'.
By "lifted to nullable", I mean that if you say:
int? x = 1;
int? y = 2;
int? z = x + y;
then you get the semantics of "if either x or y is null then z is null. If both are not null then add their values, convert to nullable, and assign the result to z."
The same goes for equality, though equality is a bit weird because in C#, equality is still only two-valued. To be properly lifted, equality ought to be three-valued: x == y should be null if either x or y is null, and true or false if x and y are both non-null. That's how it works in VB, but not in C#.
I would expect I should be able to mimic all the other behaviors of
Nullable<T>
if my code is written (almost) identically.
You are going to have to learn to live with disappointment because your expectation is completely out of line with reality. Nullable<T>
is a very special type and its magical properties are embedded deeply within the C# language and the runtime. For example:
C# automatically lifts operators to nullable. There's no way to say "automatically lift operators to MyNullable". You can get pretty close by writing your own user-defined operators though.
C# has special rules for null literals -- you can assign them to nullable variables, and compare them to nullable values, and the compiler generates special code for them.
The boxing semantics of nullables are deeply weird and baked into the runtime. There is no way to emulate them.
Nullable semantics for the
is
,as
and coalescing operators are baked in to the language.Nullables do not satisfy the
struct
constraint. There is no way to emulate that.And so on.
Null Conditional Operator with method .Value from Nullable structure
The variable?.Member
on a nullable type of a value type T
actually translates to a function roughly like
if (variable.HasValue)
return variable.GetValueOrDefault().Member;
else
return null;
variable.GetValueOrDefault()
is of the type T
, not Nullable<T>
. Only Nullable<T>
has a member Value
(unless it is your own struct that contains a member Value
, but e.g. int
from your code does not).
Practical purpose of the null-coalescing assignment operator in C#?
A real world example would be lazy loading a backing field on first access when that backing field is null. Something like this:
private string _dbQuery;
private string DbQuery => _dbQuery ??= GetQuery(queryName);
C# langversion at 7.3 but IDE still allows me to add ?? (null-coalescing operator)
Here's an excerpt from the official documentation on the operator:
In C# 7.3 and earlier, the type of the left-hand operand of the ?? operator must be either a reference type or a nullable value type. Beginning with C# 8.0, that requirement is replaced with the following: the type of the left-hand operand of the ?? and ??= operators cannot be a non-nullable value type.
In other words, the null coalescing operator does exist and is allowed in C# 7.3 and prior. Using C# 7.3 only limits you from putting a regular value type (struct or primitive) on the left side. Even here, the distinction between the two is subtle.
Basically, the only restriction in either situation is you must have a left-hand operand that is actually allowed to be null
.
C# 6.0 null-conditional operator in if statements
if items
is null, then items?.Count
is null, too.
and null == 0
is a comparison that results in false. so the if
is happy.
but items?.Any()
will also be null - and if(null)
is invalid, because null is not a boolean, and cannot be converted to one.
so you simply have to provide a fallback boolean value:
if (items?.Any() ?? false) {
}
will do the trick
Related Topics
Hook on Default "Paste" Event of Winforms Textbox Control
Datagridview Using Sortablebindinglist
Refactoring Code to Avoid Anti-Pattern
How to Create a Message Box with "Yes", "No" Choices and a Dialogresult
How to Force C# .Net App to Run Only One Instance in Windows
Why Does .Net Foreach Loop Throw Nullrefexception When Collection Is Null
Change Connection String & Reload App.Config at Run Time
Dbcontext Discard Changes Without Disposing
Recommendations on Parsing .Eml Files in C#
Bring Another Processes Window to Foreground When It Has Showintaskbar = False
Comparing Floating Point Values
Get the Final Generated HTML Source Using C# or Vb.Net
How to Unapply a Migration in ASP.NET Core with Ef Core
How to Use a Client Certificate to Authenticate and Authorize in a Web API
Httpclient and Using Proxy - Constantly Getting 407