I was wondering why this was the case, as it went against what I would have expected. This has to do with how Generic Nullable types are interpreted in C#.
public static int? MyFunction(){
return default;
}
public static T MyFunctionGeneric<T>() {
return default;
}
public static T? MyFunctionGenericNullable<T>(){
return default;
}
public static void main(string[] args){
Console.WriteLine(MyFunction());
Console.WriteLine(MyFunctionGeneric<int?>());
Console.WriteLine(MyFunctionGenericNullable<int>());
}
As expected, when I define the return type as int?
the default returned value is null
.
This is also the case when I define a generic that returns its own type, because int?
is passed in, the default returned value is null
.
However, in the third case, even though the return type is technically int?
, the default returned value is 0
. I would have to pass in int?
as my type parameter to get a null
response. Note that this happens even when I explicitly state return default(T?)
This is a little bit unexpected, does anyone have an explanation for it?
It's all very confusing because as the docs state:
The rules are necessarily detailed because of history and the different implementation for a nullable value type and a nullable reference type.
reading on in the docs, you see an answer to your question (in bold):
If the type argument for T is a reference type, T? references the corresponding nullable reference type. For example, if T is a string, then T? is a string?.
If the type argument for T is a value type, T? references the same value type, T. For example, if T is an int, the T? is also an int.
If the type argument for T is a nullable reference type, T? references that same nullable reference type. For example, if T is a string?, then T? is also a string?.
If the type argument for T is a nullable value type, T? references that same nullable value type. For example, if T is a int?, then T? is also a int?.
If we want different behavior, we need to use generic constraints. The documentation doesn't list all useful constraints which in your case would be where T:struct
This would be compiled with int?
as return type if used with int
as T
public static T? MyFunctionGenericNullable<T>()
where T : struct {
return default;
}
and you would be getting the null
like in the first two cases.