Nullable Reference Types
Append `?` to `int` and the compiler builds a whole new struct, `Nullable<int>`, carrying its own `HasValue` flag. Append the same `?` to `string` and it builds nothing new: the type is identical and only which warnings you get changes.
- ▸The same
?costs nothing onstringbut builds a whole extra struct,Nullable<int>, onint. - ▸
stringandstring?compile to the very same type; the compiler only warns you differently about each. - ▸Fix every nullable warning and null can still reach a field the compiler swore was safe.
Open a recent project file and one line is already there: <Nullable>enable</Nullable>. Switch it on in source with #nullable enable and a line you have written a thousand times changes meaning:
#nullable enable string name = null; // CS8625: cannot convert null literal to non-nullable reference type string? maybe = null; // fine: the ? says this one is allowed to be null
Turning the context on makes every bare string, User, OrderRepository non-nullable by default. You did not change the type of name; name is still a string. You changed what you are telling the compiler you intend: this reference is not supposed to hold null, so flag the line that puts null in it. Appending ? opts a single reference back into legally holding null. The result is a nullable reference type, and the on/off switch that gives ? its meaning is the nullable context.
So the feature starts as a declaration of intent, written in the type itself. FirstName as string says every person has one; MiddleName as string? says some do not. Nothing has run yet. Everything from here is the compiler reading those two characters and checking your code against them.