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

我有一种情况,我希望编译器的行为解释。 给出一点代码:

interface IFoo { T Get(); } class FooGetter : IFoo { public int Get() { return 42; } } 

以下编译并运行:

 static class FooGetterGetter { public static IFoo Get() { return (IFoo)new FooGetter(); } } 

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

 sealed class FooGetter : IFoo // etc 

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

  return (IFoo)new FooGetter(); 

的:

无法将类型’MyNamespace.FooGetter’转换为’MyNamespace.IFoo ‘

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

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

 return (IFoo)(IFoo)new FooGetter(); 

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

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

在上面的示例中, StringFoo : IFoo并且调用者要求获取int

因为FooGetterIFoo的显式实现,而不是一般地实现IFoo 。 由于它是密封的,编译器知道如果T不是int ,那么就没有办法将它IFoo转换为通用的IFoo 。 如果没有密封,编译器将允许它在运行时编译并抛出exception,如果T不是int

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

无法将“MyNamespace.FooGetter”类型的对象强制转换为“MyNamespace.IFoo”1 [System.Double]’。

我不确定的是为什么编译器不会为非密封版本生成错误。 您的子类FooGetter如何使new FooGetter()为您提供实现IFoo<{something_other_than_int}>

更新:

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

当未密封任何派生类的类可以实现IFoo

 class MyClass : FooGetter, IFoo { } 

FooGetter被标记为密封时,编译器知道除了IFoo之外的任何其他实现都不可能存在于FooGetter

这是一个很好的行为,它允许您在编译时而不是在运行时捕获代码问题。

之所以(IFoo)(IFoo)new FooGetter(); work是因为你现在将你的密封类表示为IFoo ,它可以由任何东西实现。 这也是一个很好的工作,因为你不是偶然,但有目的地覆盖编译器检查。

只是为了增加现有答案:这与使用的generics无关。

考虑这个更简单的例子:

 interface ISomething { } class OtherThing { } 

然后说(在方法内):

 OtherThing ot = XXX; ISomething st = (ISomething)ot; 

工作得很好。 编译器不知道OtherThing是否可能是ISomething ,所以当我们说它会成功时它会相信我们。 但是,如果我们将OtherThing更改为密封类型 (即sealed class OtherThing { }struct OtherThing { } ),则不再允许转换。 编译器知道它不能很顺利(除非是否为null ,但C#的规则仍然禁止从密封类型转换为未由该密封类型实现的接口)。

关于问题的更新:写入(IFoo)(IFoo)new FooGetter()与写入(IFoo)(object)new FooGetter()没有太大的不同。 你可以通过一些中间类型“允许”任何演员阵容(使用generics或不使用),这些中间类型当然/可能是你想要转换的两种类型的祖先。 它与这种模式非常相似:

 void MyMethod(T t) // no "where" constraints on T { if (typeof(T) = typeof(GreatType)) { var tConverted = (GreatType)(object)t; // ... use tConverted here } // ... other stuff }