I think the reason they did not do this is usually a bad idea to hide the inherited method using the new method with the same name and signature.
If they decide to hide Object.GetType() , the question is whether they should return a structure type of Nullable<T> or a base type. It will 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" type of runtime i is definitely Nullable<int> (also known as int? ). Of course, now the new GetType method (for example, in .NET 5.0) that returned a Nullable<int> will be a change. This is because, as it is today, i will be placed in the null link or in the int box ( not in the Nullable<int> box) due to the magic box Nullable<> .
But: Is there no reason for a (capable) programmer to use .GetType() for a variable (or other expression) such as T? compilation time T? Because he knows that the actual type coincides with the type of compile-time, namely typeof(T?) . He also knows that the base type is typeof(T) .
For the same reason, for a "normal" (non-null) value of type T , it is not useful for the 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 private types. Also, for a for a reference type that is sealed (and for which the type parameter is not covariant or contravariant) .GetType() 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<> structure are System.ValueType and System.Object , and Nullable<> does not implement any interfaces. But if our i from before is cast and put into a variable of type compilation time ValueType or object (or dynamic ), it loses its Nullable<int> identity and becomes a regular boxed type. Therefore, even if Object.GetType() was made virtual (which, of course, would be extremely dangerous), this would not even help.
Conclusion: the runtime type is Nullable<> if and only if the compile time type is Nullable<> , so this "problem" is not interesting to fix.