Search code examples
c#genericsnullabletype-constraints

Why is Nullable<T> considered a struct and not a class?


I'm trying to define a generic class that takes any type that can be set to null:

public abstract class BundledValue_Classes<T> 
    where T : class
{
    private Tuple<T, object> _bundle= new Tuple<T, object>();
    public T Value
    { 
        get { return _bundle == null ? null : _bundle.Item1; }
        set { _bundle = new Tuple<T, object>(value, obj); }
    }
    //...

This works fine. I also want to create a derived class that can take any primitive type (int, double, etc), and simply resolve that into the same as the above class, but where the primitive is wrapped in a Nullable

public abstract class BundledValue_Structs<T> 
    : BundledValue_Classes<T?> 
    where T : struct
{
   //...
}

But for some reason, this doesn't work. I get the error: The type 'T?' must be a reference type in order to use it as parameter 'T' in the generic type or method 'BundledValue_Classes<T>'

Is it clear what I'm trying to do here? I don't want to have to copy paste the contents of the whole BundledValue_Classes<T> class just to handle the case of a primitive wrapped in a Nullable<T>.

Perhaps there's some way to set the type constraint on BundledValue_Classes<T> so that it will not only accept classes, but any type that can be assigned a null value (including Nullable<T> types, which appear to be the only exception to the rule.)


Solution

  • Nullable<T> is a struct, as per the documentation. It simply isn't a class. It's a value type, which looks a bit like this in terms of fields:

    public struct Nullable<T>
    {
        private readonly T value;
        private readonly bool hasValue;
    }
    

    The null value for a Nullable<T> type isn't a null reference, it's just a value where the hasValue field is false.

    Note that Nullable<T> doesn't satisfy a condition for a constraint of where TFoo : struct however, as that's actually a non-nullable value type constraint. Basically Nullable<T> doesn't satisfy either the class or the struct constraint. There's no constraint which does only allow for nullable types (reference or Nullable<T>).