我有一种情况,我想解释编译器的行为。给出一点代码:
interface IFoo<T>
{
T Get();
}
class FooGetter : IFoo<int>
{
public int Get()
{
return 42;
}
}
以下编译并运行:
static class FooGetterGetter
{
public static IFoo<T> Get<T>()
{
return (IFoo<T>)new FooGetter();
}
}
如果我们更改Foo
类的签名并添加sealed
关键字:
sealed class FooGetter : IFoo<int> // etc
然后我在下一行遇到编译器错误:
return (IFoo<T>)new FooGetter();
中:
无法将类型'MyNamespace.FooGetter'转换为'MyNamespace.IFoo&lt; T&gt;'
有人可以解释一下sealed
关键字的问题吗?这是针对Visual Studio 2010中的.NET 4项目的C#4。
sealed
更新:只是为了澄清,当请求的return (IFoo<T>)(IFoo<int>)new FooGetter();
类型与具体类型使用的T
类型相同时,一切正常。如果类型不同,则转换会在运行时失败,例如:
无法将类型为“MyNamespace.StringFoo”的对象强制转换为类型 'MyNamespace.IFoo`1 [System.Int32]'
在上面的例子中,T
和来电者要求获得StringFoo : IFoo<string>
。
答案 0 :(得分: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 Bryant和Andras Zoltan有一些方法可以从构造函数返回派生类(或者更准确地说,编译器通过分析属性返回不同的类型) 。因此从技术上讲,如果班级没有密封,这是可行的。
答案 1 :(得分:1)
只是添加现有答案:这与使用的泛型无关。
考虑这个更简单的例子:
interface ISomething
{
}
class OtherThing
{
}
然后说(在方法内):
OtherThing ot = XXX;
ISomething st = (ISomething)ot;
工作得很好。编译器不知道OtherThing
是否可能是ISomething
,所以当我们说它会成功时它会相信我们。但是,如果我们将OtherThing
更改为密封类型(即sealed class OtherThing { }
或struct OtherThing { }
),则不再允许投射。编译器知道它不能顺利(除非ot
是null
,但C#的规则仍然禁止从密封类型转换为未由该密封类型实现的接口。
关于问题的更新:写(IFoo<T>)(IFoo<int>)new FooGetter()
与写(IFoo<T>)(object)new FooGetter()
没什么不同。您可以通过一些中间类型“允许”任何演员(使用泛型或不使用),这些中间类型当然/可能是您要在两种类型之间转换的类型的祖先。它与这种模式非常相似:
void MyMethod<T>(T t) // no "where" constraints on T
{
if (typeof(T) = typeof(GreatType))
{
var tConverted = (GreatType)(object)t;
// ... use tConverted here
}
// ... other stuff
}