Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is 'struct Nullable<T>' not a struct?

Basically why is the following invalid in C#? I can find plenty of good uses for it and in fact can fix it by creating my own nullable struct class but why and how does the C# specification (and hence the compiler) prevent it?

The below is a partial example of what I'm talking about.

struct MyNullable<T> where T : struct
{
    public T Value;
    public bool HasValue;

    // Need to overide equals, as well as provide static implicit/explit cast operators
}

class Program
{
    static void Main(string[] args)
    {
        // Compiles fine and works as expected
        MyNullable<Double> NullableDoubleTest;
        NullableDoubleTest.Value = 63.0;

        // Also compiles fine and works as expected
        MyNullable<MyNullable<Double>> NullableNullableTest;
        NullableNullableTest.Value.Value = 63.0;

        // Fails to compile...despite Nullable being a struct
        // Error: The type 'double?' must be a non-nullable value type in order to use it as parameter 'T' in the generic type or method 'ConsoleApplication1.MyNullable<T>'
        MyNullable<Nullable<Double>> MyNullableSuperStruct;
    }
}
like image 996
NtscCobalt Avatar asked Apr 17 '12 18:04

NtscCobalt


2 Answers

It is a struct. It just doesn't satisfy the value type generic type parameter constraint. From 10.1.5 of the language specification:

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.

So, the where T : struct doesn't mean what you think it means.

Basically why is the following invalid in C#?

Because where T : struct can only be satisfied by T that are non-nullable value types. Nullable<TNonNullableValueType> does not satisfy this constraint.

why and how does the compiler prevent it?

Why? To be consistent with the specification. How? By performing syntactic and semantic analysis and determining that you've supplied a generic type parameter T that doesn't satisfy the generic type constraint where T : struct.

[I] can fix it by creating my own nullable struct class but

No, you're version doesn't fix it. It's basically exactly the same as Nullable<T> except you don't get special handling by the compiler, and you're going to cause some boxing that the compiler's implementation won't box.

I can find plenty of good uses for it

Really? Such as? Keep in mind, the basic idea of Nullable<T> is to have a storage location that can contain T or can represent "the value is missing." What's the point of nesting this? That is, what's the point of Nullable<Nullable<T>>? It doesn't even make conceptual sense. That might be why it's prohibited, but I'm merely speculating (Eric Lippert has confirmed that this speculation is correct). For example, what is an int??? It represents a storage location that represents the value is missing or is an int?, which is itself a storage location that represents the value is missing or is an int? What's the use?

like image 117
jason Avatar answered Oct 12 '22 22:10

jason


One reason for the struct constraint's diallowing nullables is that we want to be able to use T? in generic methods. If struct permitted nullable value types, the compiler would have to prohibit T?.

The nullable type must have special handling in the compiler in other cases as well:

  • The null keyword must be implicitly convertible to a nullable type; this is impossible with a value type.
  • Nullable value types can be compared with the null keyword; with non-nullable value types, this comparison always returns false.
  • Nullable value types work with the ?? operator; non-nullables do not.
like image 34
phoog Avatar answered Oct 12 '22 22:10

phoog