Nullable vs nullable in C#
22 points by ehamberg
22 points by ehamberg
Yup, this is an inconvenient consequence of language evolution.
Basically, nullable reference types in C# were a complete non-starter unless libraries that adopted them did not force their callers to recompile. If they did, the whole exercise was dead in the water. The new null-annotated framework would not be usable with older libraries, libraries would not be able to ship new versions without breaking their consumers, hell would freeze over, etc etc.
So, fundamentally, nullable reference types needed to be “fake” – erased. But nullable value types are very much not erased – there’s an extra bit of data attached!
Unconstrained generics are a place where this distinction leaks through. The type parameter could either be a reference type or a value type, and you don’t know until final substitution (at runtime). But the different behavior requires picking an interpretation earlier than that. So you have two options: identify through constraints whether the type parameter is a value type or reference type, or pick an interpretation for unconstrained type parameters and stick with it.
The language design team chose the nullable reference types interpretation, partially because it was highly requested, and partially because it allowed for annotating more APIs that had the slightly weird behavior corresponding to the annotation (aside: if you aren’t rigorous about nullability from day 0, people play fast and loose with their APIs and create some weird corner cases).
There is a potential “highly principled” alternative that would have attempted to shoehorn in “true” option types throughout the framework, but that would been very expensive to implement efficiently, and also would have precluded the behavior of treating nullable analysis like a flow analysis pass, not a type check. In practice all C# users treated nullability like a flow check, e.g.
if (x != null)
{
x.Invoke(); // x is 'not null' here, which implies the variable changed type in this flow path
}
so this would have caused people to churn a lot of existing valid code.
Admittedly I like the ridgid, principle code style so this is a bit of a thorn for me, but pragmatically the current solution seems to work very well.
Nice post, and yeah, seems nuts.
P.S.: In case OP is the author, or they read this, there’re two typos: intensions instead of intentions, twice
An oddity I didn’t expect, but I’d rather have this than not have nullable reference types.