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.
But Nullable is a struct so it's supposed to be non-nullable.
Nullable<T>
is indeed a struct, but the precise meaning of the generic struct
constraint as stated in the docs is:
The type argument must be a value type. Any value type except
Nullable
can be specified. See Using Nullable Types (C# Programming Guide) for more information.
For the same reason, your line
Foo<Nullable<DateTime>> test2 = null;
results in the compiler error you are seeing, because your generic struct
constraint restricts your generic T
argument in a way so Nullable<DateTime>
must not be specified as an actual argument.
A rationale for this may have been to make calls such as
Nullable<Nullable<DateTime>> test = null;
less ambiguous: Does that mean you want to set test.HasValue
to false
, or do you actually want to set test.HasValue
to true
and test.Value.HasValue
to false
? With the given restriction to non-nullable type arguments, this confusion does not occur.
Lastly, the null
assignment works with Nullable<T>
because - as implied by the selected answers and their comments to this SO question and this SO question - the Nullable<T>
type is supported by some compiler magic.
The error is saying that the type-parameter of Nullable should be not-nullable.
What you're doing, is creating a Nullable type which has a nullable type-parameter, which is not allowed:
Nullable<Nullable<DateTime>>
is the same as
Nullable<DateTime?>
Which is quite pointless. Why do you want to have a nullable type for a type that is already nullable ?
Nullable is just a type that has been introduced in .NET 2.0 so that you are able to use ' nullable value types'. For instance, if you have a method wich has a datetime-parameter that is optional; instead of passing a 'magic value' like DateTime.MinValue, you can now pass null to that method if you do not want to use that parameter.
In generic classes where T: struct
means that type T
cannot be null.
However Nullable types are designed to add nullability to structs. Technically they are structs, but they behave like they may contain null value. Because of this ambiguity the use of nullables is not allowed with where T: struct
constraint - see Constraints on Type Parameters
Nullable types are not just generic structs with special C# compiler support. Nullable types are supported by CLR itself (see CLR via C# by Jeffrey Richter), and looks like this special CLR support makes them non-recursive.
int? i = 1; object o = i
will put int
value into variable o
and not Nullable<int>
value. In case of multiple nullables - should o = (int??)1;
contain int
or int?
value?As for C#, there are a lot of features in C# that are hard-coded for nullable types. Based on this article Nullable Types (C# Programming Guide) the primary goal of introducing nullable types is to add null support for the types that do not support nulls. Logically, since DateTime? already supports nulls it shouldn't be allowed to be "more" nullable.
This document also plainly states that
Nested nullable types are not allowed. The following line will not compile:
Nullable<Nullable<int>> n;
Special C# features of nullable types:
(int???)null ?? (int)1
resolve to (int??)1
or to (int)1
value?System.Nullable.GetValueOrDefault
property. What should it return for nested nullables?? == null
and ? != null
operators. If the Nullable<Nullable<T>>
contains Nullable<T>
value, but this value is null, what should HasValue property return? What should be the result of comparison with null?int?? i = 10
be implicitly convertible?int i = (int??)10;
supported?(bool?)null | (bool?)true == true
.So, should CLR support recursive GetType() call? Should it remove Nullable wrappers when boxing value? If it should do for one-level values, why don't for all other levels as well? Too many options to consider, too many recursive processing.
The easiest solution is to make Nullable<Nullable<T>>
non-compilable.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With