The definition of Nullable<T>
is:
[SerializableAttribute]
public struct Nullable<T> where T : struct开发者_StackOverflow中文版, new()
The constraint where T : struct
implies that T
can only be a value type. So I very well understand that I cannot write:
Nullable<string> a; //error. makes sense to me
Because string
is a reference type, not a value type. But I don't really understand why can't I write
Nullable<Nullable<int>> b; //error. but why?
Why is it not allowed? After all, Nullable<int>
is a value-type, and therefore, it can be type argument to Nullablle<T>
.
When I compiled it on ideone, it gives this error (ideone):
error CS0453: The type 'int?' must be a non-nullable value type in order to use it as type parameter 'T' in the generic type or method 'System.Nullable' Compilation failed: 1 error(s), 0 warnings
Because it's in the C# spec (section 4.4.4):
If the constraint is the value type constraint (struct), the type A must satisfy one of the following:
- A is a struct type or enum type, but not a nullable type. Note that System.ValueType and System.Enum are reference types that do not satisfy this constraint.
- A is a type parameter having the value type constraint (§10.1.5).
From section 4.1.10 of the C# language spec:
A non-nullable value type conversely is any value type other than
System.Nullable<T>
and its shorthandT?
(for anyT
), plus any type parameter that is constrained to be a non-nullable value type (that is, any type parameter with astruct
constraint). TheSystem.Nullable<T>
type specifies the value type constraint forT
(§10.1.5), which means that the underlying type of a nullable type can be any non-nullable value type. The underlying type of a nullable type cannot be a nullable type or a reference type. For example,int??
andstring?
are invalid types.
From §10.1.5 of the C# 4 spec:
The value type constraint specifies that a type argument used for the type parameter must be a non-nullable value type. All non-nullable struct types, enum types, and type parameters having the value type constraint satisfy this constraint. Note that although classified as a value type, a nullable type (§4.1.10) does not satisfy the value type constraint. A type parameter having the value type constraint cannot also have the constructor-constraint.
As others have said, the spec prohibits this.
Digging deeper, it's worth realising that you can make your own struct that allows this pattern:
struct Nestable<T> where T : struct { /* ... */ }
new Nestable<Nestable<int>>(); // This works just fine
The prohibition of nested nullables can not be expressed using the type system available to you and me. It is only enforced by a special case in the compiler (CS0453).
Aside: The new()
constraint shown in the question doesn't actually exist on System.Nullable<T>
. new()
constraints are prohibited when using the struct
constraint.
CS0451: The 'new()' constraint cannot be used with the 'struct' constraint
All structs support default initialisation anyway.
This isn't exactly an answer, but just food for thought.
Round 1
Nullable<Nullable<int>> a;
error CS0453: The type 'int?' must be a non-nullable value type in order to use it as parameter 'T' in the generic type or method 'Nullable'
Intellisense hints... The name can be simplified
Round 2
Nullable<int?>
a;
error CS0453: The type 'int?' must be a non-nullable value type in order to use it as parameter 'T' in the generic type or method 'Nullable'
Intellisense hints... The name can be simplified
Round 3
int?? a;
error CS1519: Invalid token '??' in class, struct, or interface member declaration
error CS1525: Invalid expression term '??'
Conclusion
int?
is essentially just a short-hand evaluation of Nullable<int>
, but there's no such thing as int??
which is the only way I can see of representing Nullable<Nullable<int>>
in short-hand. Plus int??
borrows the null-coalescing operator, so I'm glad it's not possible because it looks dreadful. Imagine int????????????? a;
How pointless.
Finally, since the reference source for Nullable does not yield any constraints for enforcing this, my guess is that this constraint was baked into the CLR as a special case when nullable value types were introduced into C#.
精彩评论