When I write
Nullable<Nullable<DateTime>> test = null;
I get a compilation error:
The type 'System.Datetime?' must be a non-nullable value type in order to use it as a paramreter 'T' in the generic type or method 'System.Nullable<T>'
But Nullable<T>
is a struct
so it's supposed to be non-nullable.
So I tried to create this struct
:
public struct Foo<T> where T : struct
{
private T value;
public Foo(T value)
{
this.value = value;
}
public static explicit operator Foo<T>(T? value)
{
return new Foo<T>(value.Value);
}
public static implicit operator T?(Foo<T> value)
{
return new Nullable<T>(value.value);
}
}
Now when I write
Nullable<Foo<DateTime>> test1 = null;
Foo<Nullable<DateTime>> test2 = null;
Foo<DateTime> test3 = null;
The first line is ok but for the second and third lines I get the two following compilation error:
The type 'System.DateTime?' must be a non-nullable value type in order to use it as a parameter 'T' in the generic type or method 'MyProject.Foo<T>'
(second line only)
and
Cannot convert null to 'MyProject.Foo<System.DateTime?> because it is a non-nullable value type'
Foo<Nullable<DateTime>> test = new Foo<DateTime?>();
doesn't work neither event if Nullable<DateTime>
is a struct
.
Conceptually, I can understand why Nullable<T>
is nullable, it avoids having stuffs like DateTime??????????
however I can still have List<List<List<List<List<DateTime>>>>>
…
So why this limitation and why can't I reproduce this behavior in Foo<T>
? Is this limitation enforced by the compiler or is it intrinsic in Nullable<T>
code?
I read this question but it just says that it is not possible none of the answers say fundamentally why it's not possible.
Best Answer
Nullable<T>
is indeed a struct, but the precise meaning of the genericstruct
constraint as stated in the docs is:For the same reason, your line
results in the compiler error you are seeing, because your generic
struct
constraint restricts your genericT
argument in a way soNullable<DateTime>
must not be specified as an actual argument.A rationale for this may have been to make calls such as
less ambiguous: Does that mean you want to set
test.HasValue
tofalse
, or do you actually want to settest.HasValue
totrue
andtest.Value.HasValue
tofalse
? With the given restriction to non-nullable type arguments, this confusion does not occur.Lastly, the
null
assignment works withNullable<T>
because - as implied by the selected answers and their comments to this SO question and this SO question - theNullable<T>
type is supported by some compiler magic.