我有一种情况,我希望编译器的行为得到解释。给出一小段代码:
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”
有人能解释一下这里发生的关于sealed
关键字的事情吗?这是Visual Studio2010中针对.NET 4项目的C# 4。
更新:有趣的是,当我想知道为什么下面的代码在应用sealed
时会修复它时,我偶然发现了这部分行为:
return (IFoo<T>)(IFoo<int>)new FooGetter();
更新:只是为了澄清,当请求的T
类型与具体类型使用的T
类型相同时,它都运行得很好。如果类型不同,则强制转换在运行时会失败,并显示如下内容:
无法将'MyNamespace.StringFoo‘类型的对象强制转换为'MyNamespace.IFoo`1System.Int32’类型
在上面的例子中,StringFoo : IFoo<string>
和调用者请求获取一个int
。
发布于 2012-01-18 23:33:01
因为FooGetter
是IFoo<int>
的显式实现,而不是一般的IFoo<T>
实现。因为它是密封的,所以如果T
不是int
,编译器就知道没有办法将它转换为泛型IFoo<T>
。如果它不是密封的,如果T
不是int
,编译器将允许它在运行时编译并抛出异常。
如果您尝试将它与int
(例如FooGetterGetter.Get<double>();
)以外的任何对象一起使用,则会得到一个异常:
无法将'MyNamespace.FooGetter‘类型的对象强制转换为’MyNamespace.IFo`1System.Double‘类型。
我不确定的是,为什么编译器不会为非密封版本生成错误。像new FooGetter()
这样子类FooGetter
如何提供实现IFoo<{something_other_than_int}>
的任何东西
更新:
对于Dan Bryant和Andras Zoltan,都有从构造函数返回派生类的方法(或者更准确地说,编译器通过分析属性来返回不同的类型)。因此从技术上讲,如果类不是密封的,这是可行的。
发布于 2012-01-19 01:21:09
当一个类处于未密封状态时,任何派生类都可以实现IFoo<T>
class MyClass : FooGetter, IFoo<double> { }
当FooGetter
被标记为密封时,编译器知道除了IFoo<int>
之外,对于FooGetter
不可能存在任何其他的IFoo<T>
实现。
这是一个很好的行为,它允许您在编译时而不是运行时捕获代码中的问题。
(IFoo<T>)(IFoo<int>)new FooGetter();
工作的原因是因为您现在将您的密封类表示为IFoo<int>
,它可以由任何东西实现。这也是一个很好的解决办法,因为你不是偶然的,而是故意覆盖编译器检查。
发布于 2012-10-15 05:02:39
只是补充一下现有的答案:这与使用的泛型没有任何关系。
考虑这个更简单的例子:
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
}
https://stackoverflow.com/questions/8912542
复制相似问题