I've come across the following phenomenon and am absolutely bamboozled. I'm using C# 10 with nullables enabled.
default(int?)
returns null as expected. The following function, however, returns whatever default(T)
is
public static T? ShouldReturnNull<T>()
{
return default(T?);
}
In the case of ShouldReturnNull<int>()
we get 0. Shouldn't it also return null?
I have the following code in my program where this becomes an issue:
public T?[] FindKElements(...)
{
var result = new (T, double)?[k];
// ... populate result array,
// possibly including null values...
// return an array containing only the T part or null
return result.Select(e => e is null ? default(T?) : e.Value.Item1).ToArray();
}
Is there a way to keep it this generic but with proper nulls instead when T is a value type? The compiler won't let me use null
in place of default(T?)
.
CodePudding user response:
In the absence of a where T : struct
constraint, T?
does not mean Nullable<T>
; it means "T
, but note that it won't be null
in the NRT sense" - and since NRT nulls never apply to your value-type scenario: it basically just means T
; and the default value of a value-type T
is not null
(in any sense).
In the scenario where you need "null checking" that crosses both value-type and reference-type scenarios including support for value-types without a value, then the easiest approach is usually to forget about Nullable<T>
and just track:
- do I have a value (
bool
), and - what is the value (
T
)
separately, and explicitly; this could be via any of:
bool SomeMethod(out var T)
(HasValue: bool, Value: T) SomeMethod()
Maybe<T> SomeMethod()
(where Maybe<T>
is just a custom struct
that is composed of a bool HasValue
and a T Value
)
This is effectively creating something akin to Nullable<T>
, but which applies to all values, regardless of type. Instead of checking for null, just check .HasValue
first, and if true, assume that the value is meaningful.