[C#] The Nullable paradox, part 1

Did you ever try to use a Nullable as a generic type parameter ? Well it works as expected… as soon as you don’t have any type constraint.

A reference type ?

Let’s try with a simple example with a class constraint.

T DoSomething<T>() where T : class
{
	return null;
}

This is OK, I can return null, since T is a reference type.

Now if I want to use a nullable type as T, like this:

DoSomething<int?>();

It sounds ligit at first: since I can set a nullable to null, maybe it matches the class constraint… Well, it doesn’t. Here it what the compiler says:

The type 'int?' must be a reference type in order to use it as parameter 'T' in the generic type or method 'DoSomething<T>()'

That’s completely understandable because Nullable<T> is a struct, everybody knows that.

A value type ?

### So int? is a value type, I can deal with that. Let’s see how it behaves with a struct constraint:

T DoSomething<T>() where T : struct
{
	return default(T);
}

OK, now I call that method like this:

DoSomething<int?>();

Compile, and…

The type 'int?' must be a non-nullable value type in order to use it as parameter 'T' in the generic type or method 'DoSomething<T>()'

Well, that’s unexpected !

Another constraint ?

As we saw, as far as generic type constraints are conserned, nullable types are neither class nor struct.

So let’s try to write a type constraint to specifically target nullable types:

T DoSomething<T,U>() 
    where T : Nullable<U>
	where U : struct
{
	return default(T);
}

But if you compile that, you’ll get:

'U?' is not a valid constraint. A type used as a constraint must be an interface, a non-sealed class or a type parameter.

And that’s right, you can’t use a struct type (like Nullable) as a type constraint. It make sense, since a struct can’t be inherited, there is no point in specifying it as a constraint, you could simply use the actual type in the first place.

You can turn this problem over an over, but as far as I know, you can’t use nullable types when there is a type constrain.

So eventually, you’ll have to remove the constraint.

Now, why ?

Let’s look at the definition:

public struct Nullable<T> where T : struct
{
    ...
}

We can easly understand why it’s a struct. It’s also obvious why they added a type constraint.

Apparently, nothing prevents me from writing this:

Nullable<Nullable<int>> i;

But in reality, it generate the following compilation error:

The type 'int?' must be a non-nullable value type in order to use it as parameter 'T' in the generic type or method 'System.Nullable<T>'

Sounds familiar, doesn’t it ?

So we may have find one of the reasons of this peculiarity. It makes no senses to create a Nullable of Nullable, so maybe they changed the meaning of where T : struct into where T : struct, not Nullable, to prevent this.

I think that’s one of the reasons but not the only one. We’ll see another explanation in next part.