Although C# pretends that value-type storage locations hold instances of types derived from System.ValueType, which in turn derives from System.Object, that isn't really true.  Each type derived from System.ValueType actually represents two very different kinds of things:
- A collection of bytes which (for primitive types) represents the data directly, or (for non-primitive structure types) holds the contents of all fields, public and private, but does not hold any type information.
 - A standalone heap object, which contains an object header in addition to the above, whose type is derived from `System.ValueType`.
 
Storage locations of a value type hold the first; heap objects of a value type hold the second.
For various reasons, Microsoft decided that Nullable<T> should only support the first usage.  If one attempts to pass a storage location of type Nullable<T> to code which expects a reference to a heap object, the system will convert the item to a T if HasValue is true, or else simply pass a null reference if HasValue is false.  While there are ways to create a heap object of type Nullable<T>, the normal methods of converting a value-type storage location to a heap object will never generate one.
Note also that calling GetType() on a value storage location won't actually evaluate the type of the storage location, but will instead convert the contents of that storage location to a heap object and then return the type of the resulting object.  Because storage locations of type Nullable<T> get converted either to object instances of T or to null, nothing in an object instance will say whether the storage location from which it came was a Nullable<T>.