C#’s can’t make `notnull` type nullable

Basically you’re asking for something that can’t be represented in IL. Nullable value types and nullable reference types are very different beasts, and while they look similar in source code, the IL is very different. The nullable version of a value type T is a different type (Nullable<T>) whereas the nullable version of a reference type T is the same type, with attributes telling the compiler what to expect.

Consider this simpler example:

public class Foo<T> where T : notnull
{
    public T? GetNullValue() => 
}

That’s invalid for the same reason.

If we constraint T to be a struct, then the IL generated for the GetNullValue method would have a return type of Nullable<T>.

If we constraint T to be a non-nullable reference type, then the IL generated for the GetNullValue method would have a return type of T, but with an attribute for the nullability aspect.

The compiler can’t generate IL for a method which has a return type of both T and Nullable<T> at the same time.

This is basically all the result of nullable reference types not being a CLR concept at all – it’s just compiler magic to help you express intentions in code and get the compiler to perform some checking at compile-time.

The error message isn’t as clear as it might be though. T is known to be “a value type or non-nullable reference type”. A more precise (but significantly wordier) error message would be:

A nullable type parameter must be known to be a value type, or known to be a non-nullable reference type. Consider adding a ‘class’, ‘struct’, or type constraint.

At that point the error would reasonably apply to our code – the type parameter is not “known to be a value type” and it’s not “known to be a non-nullable reference type”. It’s known to be one of the two, but the compiler needs to know which.

Leave a Comment