In C, one uses the token .
to access a member of the left operand, and ->
to access a member of the thing to which the left operand holds a pointer (reference). A null reference occurs if the left-hand operand of ->
is null. In C#, the .
has the former meaning when applied to value types and the latter meaning when applied to class types; using .
in C# on a class type variable which is null will trigger a NullReferenceException
.
The .NET type Nullable<T>
is a two-field structure which contains Boolean field which indicates whether it has a meaningful (non-null) value, and field of value type T
to say what that value is (if not null). Although the compiler will allow one to assign a Nullable<T>
to null or compare one with null, the null
in the former case is really just shorthand for a default instance (where the "has-value" flag is false, and the value field holds the default value for type T), and the comparison to null is really just a shorthand for checking the HasValue
property (which in turn looks at the Boolean
field). Invoking ToString
on e.g. a Nullable<int>
which is "null" is no different semantically from invoking it on any other structure that contains a Boolean
and an Int32
which have the values False
and 0
, respectively. There are a few ways in which compilers treat nullable types "specially", but a default-valued Nullable<int>
holds [False, 0]
, not a null reference.
Note that the first example throws because of one of the "special" rules the framework applies to nullable types. Casting a value type to Object
or another reference type generally instructs the runtime to create an object that will behave as a class object with fields and members matching those of the struct and return a reference to that new object--a process known as "boxing". Because the designers of nullable types wanted to allow code to say if (NullableThing == null)
rather than the (IMHO sematically clearer) if (!NullableThing.HasValue)
, the runtime was implemented so that boxing a nullable whose HasValue
field is false will yield a null reference rather than a reference to a default-valued instance of Nullable<T>
. While I think there are cases where would make sense to have some value types use different box and unbox methods, nullable types are the only ones that can use anything other than normal boxing.