While reference types can be null (when they refer to no object) value types can not.
This is because a reference type variable is a "pointer" to a memory address and so one such address is reserved to be "null" but in the case of a value type there is no space available to signify the lack of a value - somebody declaring byte for example will want all 256 possible values.
In C# 1 you had to choose between adding a "missing value" flag or reserving a special value to indicate the absence of a value.
A Nullable<T> type was added that wraps the value type with a boolean to flag the presence or absence of a value. The compiler allows the result to be treated as having null semantics, provided a shortcut ? operator to be placed after existing type references to indicate they may be null and allows default(T) to allow a type to be initialized to either null, 0 or 0.0 depending on the type it references
Code
int? quantity = int.TryParse(txtQuantity.Value, out var parsed)
? parsed
: null;
updateOrderLine(quantity);int quantity = 0;
bool quantityProvided = txtQuantity.Value != ""
&& int.TryParse(txtQuantity.Value, out quantity);
updateOrderLine(quantityProvided ? quantity : -1); // -1 = missingNotes
An alternative way to achieve this in C# 1 was to use the NullableTypes open-source project that provided hand-crafted wrapper types for each of the built-in .NET value types.