First introduced in .NET 2, very little has changed for Nullable<T>
over the last decade and a half, but that looks like it is about to change. Much of the work going into supporting nullable reference types in C# 8 may be applied to nullable value types.
First up is null state tracking proposal, the feature which allows the compiler to detect code that may fail because the necessary null checks haven’t been performed. As with nullable reference types, an NVT used in an unsafe manner will be noted as a compiler warning. In order to support this, the method Nullable<T>.HasValue
will be treated the same as any other null check.
A more difficult question is whether or not to allow a nullable value to be used as if it were a normal, non-nullable value, when it’s known to be non-null. This would remove the need to use .Value
or .GetValueOrDefault()
members. The proposal continues:
One immediate obstacle is that NVTs are types in their own right, with their own members and a separate type identity wrt. overload resolution. For member access and conversions, they already have semantics, and any new semantics where they "pose" as the underlying type would have to be strictly additional and non-breaking. So they would kick in only in places where you'd get an error today.
For simplicity we should probably allow the additional member accesses and conversions regardless of whether the value is null or not, but then warn on it when they are applied to a nullable value that has the "may be null" null state. Allowing it regardless of null state also helps maintain the design principle from NRTs that the null state should never affect semantics, only whether warnings are yielded.
If adopted, this will require shadowing some methods.
For back compat, members that are defined on Nullable<T> should always shadow corresponding members on the underlying type. That is a pretty short list, though, and many of them (ToString, Equals, GetHashCode) work by calling through to the underlying type when the value is non-null, so only very few members of the underlying value would be effectively shadowed in the sense that the behavior is different. Those are members directly implementing the contract of the NVT (Value, HasValue, GetValueOrDefault), as well as reflection (GetType), which must acknowledge that a nullable value is different at runtime from its underlying value.
Ultimately this will lead to a design guideline stating structs should not implement their own Value, HasValue, GetValueOrDefault members.
The current recommendation is to implement null tracking and defer the latter feature until a later version of C#.