Trying to get a better understanding of why this is a language feature:
We have:
public static DateTime? Date { get; set; }
static void Main(string[] args)
{
Date = new DateTime(2017, 5, 5);
Console.WriteLine(Date.Value.Date);
Console.Read();
}
Why do I need to use Value to take the value from the nullable type? It's not like it checks for null before calling Date, if the value is null it will throw a NullReference exception. I get why .HasValue can work,
but am unsure about why we need .Value on each nulllable type?
This is due to how nullable types are implemented.
The questionmark syntax only translates into Nullable<T>
, which is a struct you could very well write yourself (except for the …?
syntax being a language feature for this type).
The .NET Core implementation of Nullable<T>
is open source and its code helps explaining this.
Nullable<T>
only has a boolean field and a value field of the underlying type and just throws an exception when accessing .Value
:
public readonly struct Nullable<T> where T : struct
{
private readonly bool hasValue; // Do not rename (binary serialization)
internal readonly T value; // Do not rename (binary serialization)
…
public T Value
{
get
{
if (!hasValue)
{
ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_NoValue);
}
return value;
}
}
…
When you do a cast / assignment like DateTime aDateTime = (DateTime)nullableDateTime
, then you only call an operator that is defined on the same class which works exactly like an operator defined on a custom type. This operator only calls .Value
so the cast hides the access to the property:
public static explicit operator T(Nullable<T> value)
{
return value.Value;
}
There also is an operator for the inverse assignment, so DateTime? nullableNow = DateTime.Now
would call:
public static implicit operator Nullable<T>(T value)
{
return new Nullable<T>(value);
}