Does the "?." operator do anything else apart from checking for null?

3.1k Views Asked by At

As you might know, DateTime? does not have a parametrized ToString (for the purposes of formatting the output), and doing something like

DateTime? dt = DateTime.Now;
string x;
if(dt != null)
    x = dt.ToString("dd/MM/yyyy");

will throw

No overload for method 'ToString' takes 1 arguments

But, since C# 6.0 and the Elvis (?.) operator, the above code can be replaced with

x = dt?.ToString("dd/MM/yyyy");

which.... works! Why?

3

There are 3 best solutions below

0
On BEST ANSWER

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

14
On

Short answer:

DateTime? is only a sweet syntax for Nullable<DateTime> which doesn't contain DateTime's properties and method while the Elvis operator works on the not-Nullable Nullable<DateTime>.Value.


Explanation:

The following code:

DateTime? dt = DateTime.Now;
string x;
if (dt != null)
    x = dt?.ToString("dd/MM/yyyy");

When decompiles as C# 5.0 yields the following result:

DateTime? nullable = new DateTime?(DateTime.Now);
if (nullable.HasValue)
{
    string str = nullable.HasValue ? nullable.GetValueOrDefault().ToString("dd/MM/yyyy") : null;
}

Side note: the string seems declared inside the if is irrelevant because of hoisting at the MSIL level, and since the value is not used later on the decompiler shows it as if it was declared inside that if scope.

As you see, and since DateTime? is only a sweet syntax for Nullable<DateTime>, C# has a specific reference for Nullable<T>s with the Elvis operator, making its return value the non-nullable T itself.

The result of the whole Elvis operator must be Nullable therefore, if you wanted to receive a non-string value it would have to be either Nullable<T> or a ReferenceType but this doesn't change the fact that if the operator has managed to get the Nullable<DateTime> value itself - the returned DateTime is not Nullable<DateTime> anymore.

0
On

Considering:

DateTime? dt = DateTime.Now;
string x;
if(dt != null)
    x = dt.ToString("dd/MM/yyyy");

Here dt is a DateTime? or Nullable<DateTime> witch is not IFormatable and don't have a ToString(string format) method.

So it throw.

Now considering:

x = dt?.ToString("dd/MM/yyyy");

The ?. is a syntactic sugar for:

dt.HasValue ? dt.Value.ToString("dd/MM/yyyy"): null

Here dt.Value is a DateTime witch is IFormatable and have a ToString(string format) method.

Finally the good way to write the first code in C# 5.0 is:

DateTime? dt = DateTime.Now;
string x;
if(dt.HasValue)
    x = dt.Value.ToString("dd/MM/yyyy");