I have this code snippet
public static void Main()
{
var t = new Test<uint>();
t.Run(null);
}
public class Test<T> where T: notnull
{
public void Run(T? value)
{
Console.WriteLine(value is null);
}
}
Where I want to be able to say that T is not nullable but for my method I want to accept nullable types. However this does not work as the signature of Run is uint
not uint?
.
This is not the case with reference types and they work as I expect being able to have a nullable signature.
What is going on here that doesn't allow values types to have nullable method signatures. I found if I change from notnull to 'struct' it works fine, but I want to be able to accept reference and value types.
What is going on here that doesn't allow values types to have nullable method signatures.
There's be no way to represent this in IL, basically.
There's a fundamental difference between what T?
means in the case of Test<int>
vs Test<string>
. In the former case, T?
means a completely different type, Nullable<int>
whereas in the latter, T?
means "string
as far as the CLR is concerned, but allowing null during compile-time null analysis".
You can definitely get into some odd situations with T?
and the notnull
constraint - the constraint is useful, but it's definitely not what you might want or expect it to be. For example, you can declare a field of type T?
, but it ends up just being T
in the IL:
public class Test<T> where T : notnull
{
private T? field;
public Test(T value) => field = value;
public Test() {}
public bool FieldIsNull => field == null;
}
class Program
{
static void Main()
{
Test<string> test1 = new Test<string>();
Console.WriteLine(test1.FieldIsNull); // True
Test<int> test2 = new Test<int>();
Console.WriteLine(test2.FieldIsNull); // False
}
}
If it's any consolation, the ECMA C# standardization group is working hard to specify this "cleanly" - and it's very tricky...