密封关键字会影响编译器对强制转换的看法

Ada*_*rth 19 c# compiler-errors visual-studio-2010

我有一种情况,我喜欢编译器的行为解释.给出一点代码:

interface IFoo<T>
{
    T Get();
}

class FooGetter : IFoo<int>
{
    public int Get()
    {
        return 42;
    }
}
Run Code Online (Sandbox Code Playgroud)

以下编译并运行:

static class FooGetterGetter
{
    public static IFoo<T> Get<T>()
    {
        return (IFoo<T>)new FooGetter();
    }
}
Run Code Online (Sandbox Code Playgroud)

如果我们更改Foo类的签名并添加sealed关键字:

sealed class FooGetter : IFoo<int> // etc
Run Code Online (Sandbox Code Playgroud)

然后我在以下行得到编译器错误:

 return (IFoo<T>)new FooGetter();
Run Code Online (Sandbox Code Playgroud)

的:

无法将类型'MyNamespace.FooGetter'转换为'MyNamespace.IFoo <T>'

有人可以解释sealed关键字的问题吗?这是针对Visual Studio 2010中的.NET 4项目的C#4.

更新:有趣的是,当我想知道为什么以下代码在sealed应用时修复它时,我偶然发现了这部分行为:

return (IFoo<T>)(IFoo<int>)new FooGetter();
Run Code Online (Sandbox Code Playgroud)

更新:只是为了澄清,当T请求的类型T与具体类型使用的类型相同时,它都运行良好.如果类型不同,则转换在运行时失败,例如:

无法将"MyNamespace.StringFoo"类型的对象强制转换为"MyNamespace.IFoo"1 [System.Int32]'

在上面的示例中,StringFoo : IFoo<string>调用者要求获取int.

D S*_*ley 8

因为FooGetter是一个明确的实现IFoo<int>而不是IFoo<T>一般地实现.由于它是密封的,编译器知道没有办法将它转换为泛型,IFoo<T>如果T不是一个int.如果没有密封,编译器将允许它编译并在运行时抛出异常(如果T不是)int.

如果你尝试使用除int(例如FooGetterGetter.Get<double>();)以外的任何东西,你会得到一个例外:

无法将类型为"MyNamespace.FooGetter"的对象强制转换为"MyNamespace.IFoo"1 [System.Double]'.

什么我不知道的是为什么编译器产生非密封版本错误.怎么可能你的子类FooGetter,从而new FooGetter()给你什么实现IFoo<{something_other_than_int}>

更新:

Per Dan BryantAndras Zoltan有一些方法可以从构造函数返回派生类(或者更准确地说,编译器通过分析属性返回不同的类型).所以从技术上讲,如果班级没有密封,这是可行的.

  • +1但是要回答你的上一个问题 - 好吧 - 它是一个界面 - 所以你可以添加另一个界面实例?编译器没有看到`new`它只看到一个类型为`FooGetter`的表达式,当不是`sealed`时,它可以是从它派生的任何类型. (4认同)
  • 为了充实安德拉斯的观点,你可以拥有`SecondFoo:FooGetter,IFoo <double>`.好奇为什么它在这种情况下忽略了`new`,它保证没有派生类型......除非它假设一个基类型可能实现正确的接口并且不打扰看 - 将错误留给运行时. (3认同)
  • 编译器将为除除泛型之外的其他类型的强制转换的密封类进行这些静态推断.例如,如果你有一个完全没有实现IFoo的密封类,它会正确地抱怨.但是,如果同一个类是未密封的,则可能存在实现接口的派生类,因此它允许尝试进行转换.这似乎是静态分析的限制,其中编译器决定转换是不可能的.我相信密封类实现接口时也会发生一些优化,所以也许这是相关的. (3认同)