[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.