int? and string?
An int variable always holds a number. But sometimes there is no honest number: a user did not give their age, a sensor did not send the temperature. Zero would lie here: an age of 0 is an age too.
You cannot put null into an int. A value type holds the value itself, not a reference, and the line int age = null; does not compile: the compiler answers with error CS0037. The same goes for your struct: the line Point p = null; does not compile.
Picture a form where the "Age" field has a "filled in" checkbox next to it. For this, C# has types with a question mark: int? holds a number and a mark saying whether it is there.
The int? type
A nullable type is a value type with a ? sign: int?, double?, bool?. The full name of int? is Nullable<int>. It is a struct of two parts: the value itself and a flag saying whether it is there.
int? age = null;
Console.WriteLine(age.HasValue); // False
Console.WriteLine(age ?? -1); // -1
age = 30;
Console.WriteLine(age.HasValue); // True
Console.WriteLine(age.Value + 1); // 31Let's go through the example line by line:
int? age = null;—the field is empty, the box is not ticked.age.HasValueprintsFalse: there is no value.age ?? -1supplies-1. The??operator works withint?the same way as with references.- After
age = 30there is a value, andHasValueprintsTrue. age.Valuereads the number, soage.Value + 1prints31.
Here the form and int? part ways. On paper, you can write in a number and forget to tick the box. In int?, the box ticks itself: assign a number, and HasValue becomes true.
int? and int are different types. The line int x = age; does not compile (error CS0266): age may turn out to hold no number. You take the number out explicitly: with age ?? 0, or with age.Value once you have checked HasValue.
?. gives an int?
A string's property Length has the type int. But if there is no string, there is no length either, so word?.Length gives an int?:
string? word = null;
int? length = word?.Length;
Console.WriteLine(length.HasValue); // False
int safe = word?.Length ?? 0;
Console.WriteLine(safe); // 0With null in word, word?.Length gives an empty int?, and HasValue prints False. To get a plain int, you add ?? 0: then 0 replaces the missing value.
Reference types and string?
A reference variable can always hold null: that is how references work. So for a reference type, the ? sign means something else—it is a hint for the compiler:
string—nullis not expected here;string?—nullmay end up here.
New projects and programs run with dotnet run hello.cs have a check called nullable reference types turned on. The compiler uses these hints to track where null may get to, and it warns about the dangerous spots:
string? nick = null;
string name = nick; // ✗
Console.WriteLine(nick.Length); // ✗warning CS8600: Converting null literal or possible null value to non-nullable type.
warning CS8602: Dereference of a possibly null reference.What the two warnings mean:
CS8600—a possiblenullgoes into a variable of a type without?;CS8602—a member is accessed through a variable that may holdnull.
These are warnings, not errors: the program compiles and runs. But the third line stops it at once with a NullReferenceException, the one CS8602 warned about. The warning goes away when the compiler sees a check: inside if (nick != null), you may access nick.Length.
int? or string?
Interviewers like to ask: how does int? differ from string?? The answer has two parts:
int?is a separate type,Nullable<int>: a struct with a value and aHasValueflag that exists while the program runs too;string?is the same typestringwith a note for the compiler: at run time there is no difference betweenstringandstring?, and only the compiler warns aboutnull.
Common mistake
Someone read Value without checking whether there is a value:
int? score = null;
Console.WriteLine(score.Value); // ✗Unhandled exception. System.InvalidOperationException: Nullable object must have a value.The compiler warns about this line (CS8629: Nullable value type may be null.), but it compiles the program. When it runs, Value on an empty int? stops it with an InvalidOperationException. Check HasValue before Value, or take score ?? 0.
Key points
int?is a value type that can holdnull:HasValuesays whether there is a value, andValuereads it.Valueon an emptyint?throws anInvalidOperationException; reading through??is safe.string?is a note for the compiler: it warns withCS8600andCS8602, but the program compiles.int?is a separate type,Nullable<int>, whilestringandstring?are the same at run time.