I think the reason why they didn't do that is that it is generally a bad idea to hide an inherited method with a new method with the same name and signature.
If they chose to actually hide Object.GetType(), the question is if they should return the type of the Nullable<T> struct, or the underlying type. This would be either:
public struct Nullable<T> where T : struct
{
....
public new Type GetType()
{
return typeof(Nullable<T>);
}
}
or:
public struct Nullable<T> where T : struct
{
....
public new Type GetType()
{
return typeof(T);
}
}
If you have:
int? i = null;
or:
int? i = 42;
the "real" runtime type of i is certainly Nullable<int> (also known as int?). Of course, introducing a new GetType method now (in .NET version 5.0, say) which returned Nullable<int> would be a breaking change. That's because the way it is today, the i will be boxed into either a null reference or a boxed int (not a boxed Nullable<int>) because of the magical boxing of Nullable<>.
But: There is really no reason for a (capable) programmer to ever use .GetType() on a variable (or other expression) of compile-time type T?, because he knows the actual type is the same as the compile-time type, namely typeof(T?). He also knows that the underlying type is typeof(T).
For the same reason, for a "usual" (not nullable) value type T, it is not useful for a programmer to use .GetType() when the compile-time type is T, because he knows the result will always be typeof(T). This is because all value types are sealed types. Also, for a for a reference type which is sealed (and for which no type parameter is co- or contravariant), .GetType() is useless.
To give an example:
string str = ...;
...
var t = str.GetType(); // This is really useless. If str is null
// an exception is thrown. Otherwise the
// t will ALWAYS be typeof(string) because
// the class System.String is a sealed class.
The base classes of the Nullable<> struct are System.ValueType and System.Object, and Nullable<> implements no interfaces. But if our i from before is cast and put into a variable of compile-time type ValueType or object (or dynamic), it loses its identity of Nullable<int> and becomes an ordinary boxed value type. Therefore, even if Object.GetType() were made virtual (which would of course be extremely dangerous) it wouldn't even be helpful.
Conclusion: The runtime type is Nullable<> if and only if the compile-time type is Nullable<>, so this "problem" is not interesting to fix.