? (Nullable) Operator in C#

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



Leave a reply



Submit