为什么Nullable<>不能隐藏GetType?

4

从这个问题为什么n.GetHashCode()可以工作,但n.GetType()会抛出异常? Jon给出的答案引导我提出了这个问题:为什么Nullable<>没有隐藏GetType

public new Type GetType()
{
    return GetValueOrDefault().GetType();
}

因为这样

,然后

int? i = null;
Console.WriteLine(i.GetType().Name);

应该可以正常工作,对吧?我是不是漏掉了什么明显的东西?有哪些注意事项?我已经尝试在Google上搜索,但没有找到令人满意的解释。

更新:为了澄清一点。这个可以正常工作:

int? i = null;
Console.WriteLine(i.GetHashCode());

唯一的原因是 i.GetType() 抛出异常是因为 GetType 不是虚拟的,无法覆盖。所以当调用它时,i 被装箱成对象,导致结果为 null,然后抛出异常。但是如果实现了 Nullable,就像这样:
 public struct Nullable<T> where T : struct
 {
     ....
     public new Type GetType()
     {
         return GetValueOrDefault().GetType();
     }
 }

那么这将使行为更加一致(个人认为),因为所有这些调用都可以工作,而不仅仅是前两个调用:

 int? i = null;
 Console.WriteLine(i.GetHashCode());
 Console.WriteLine(i.ToString());
 Console.WriteLine(i.GetType());

2
你如何隐藏继承自Object的方法? - Kevin
1
此外,你为什么想要这样做呢? - Grant Thomas
3
没错,为什么 'null.GetType()' 会抛出 null 引用异常 :) - Poma
1
int?Nullable<int> 的语法糖,它是一个值类型。你可以将 null 赋值给它的唯一原因是因为 CLR 中实现了特殊的行为。所以 i.GetType() 在技术上 不等于 null.GetType()。由于上述原因和我在问题中提到的原因,最终结果是这样的,尽管我认为这可以避免,但事实并非如此。因此,我的问题。 - ChrisWue
2个回答

2

我认为这是因为GetType返回当前实例的确切运行时类型。在这种情况下,它没有运行时类型,因为它引用了null

考虑下面的例子:

  MyBaseClass myBase = null;
  MyDerivedClass myDerived = null;
  object o = myDerived;
  MyBaseClass b = myDerived;

如果myBase.GetType()返回MyBaseClassmyDerived.GetType()返回MyDerivedClass,那么o.GetType()b.GetType()应该返回什么? Nullable不简单地隐藏object.GetType并在其为null时返回其编译时类型的原因,可能是因为这会破坏GetType的约定。

如上所述,int? 相当于 Nullable<int>,并且当您在 Nullable<T> 上调用 GetType 时,它将返回 typeof(T)。这是现有的行为。 - ChrisWue
@ChrisWue 不,它不会返回 typeof(T),它调用了从 object 继承的 GetType 并返回其运行时类型。请参见:http://msdn.microsoft.com/en-us/library/b3h38hb0.aspx。也许你和 Type.GetType 搞混了? - Rian Schmits
好的,也许我表达得不太对,但是当在 Nullable<int> 上调用 GetType 时,它返回基础类型。 - ChrisWue
这个答案不好。它说“它没有运行时类型,因为它引用了null”,但这是不正确的。它是一个值类型,有一个具有运行时类型的对象。此外,为了让自己相信当前的行为会使很多人感到困惑,请看看与线程Nullable type is not a nullable type?相关联的问题数量。今天这个调用有点给出了错误的答案,但对于一个值类型来说,没有真正需要进行这个GetType()调用。请看看我的新答案。 - Jeppe Stig Nielsen

0

我认为他们没有这样做的原因是,用具有相同名称和签名的new方法隐藏继承方法通常是一个不好的想法。

如果他们选择实际隐藏Object.GetType(),那么问题就是他们应该返回Nullable<T>结构的类型还是基础类型。这将是以下之一:

public struct Nullable<T> where T : struct
{
    ....
    public new Type GetType()
    {
        return typeof(Nullable<T>);
    }
}

或者:

public struct Nullable<T> where T : struct
{
    ....
    public new Type GetType()
    {
        return typeof(T);
    }
}

如果你有:

int? i = null;

或者:

int? i = 42;

i的“真实”运行时类型肯定是Nullable<int>(也称为int?)。当然,现在引入一个新的GetType方法(比如在.NET 5.0中)返回Nullable<int>将会是一个破坏性的变化。这是因为现在的方式是,由于Nullable<>的神奇装箱,i将被装箱成null引用或装箱的int(而不是装箱的Nullable<int>)。

但是:对于编译时类型T?的变量(或其他表达式),有能力的程序员实际上没有理由使用.GetType(),因为他知道实际类型与编译时类型相同,即typeof(T?)。他还知道底层类型是typeof(T)

出于同样的原因,对于一个“通常”(非可空)值类型T,当编译时类型为T时,程序员使用.GetType()是没有用处的,因为他知道结果总是typeof(T)。这是因为所有值类型都是密封类型。此外,对于一个密封的引用类型(且没有协变或逆变类型参数),.GetType()也是无用的。
举个例子:
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.

< p > Nullable<> 结构的基类是 System.ValueTypeSystem.Object,而且 Nullable<> 没有实现任何接口。但是,如果我们之前的 i 被强制转换并放入编译时类型为 ValueTypeobject(或 dynamic)的变量中,它将失去其 Nullable<int> 的身份,成为一个普通的装箱值类型。因此,即使 Object.GetType() 被设置为 virtual(这当然非常危险),它也没有帮助。

结论:只有在编译时类型为 Nullable<> 时,运行时类型才是 Nullable<>,因此这个“问题”没有修复的必要。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接