密封关键字会影响编译器对强制转换的意见
我有一种情况,我希望编译器的行为解释。 给出一点代码:
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
。
因为FooGetter
是IFoo
的显式实现,而不是一般地实现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
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
没有太大的不同。 你可以通过一些中间类型“允许”任何演员阵容(使用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 }