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

时间:2012-01-18 15:16:14

标签: c# visual-studio-2010 compiler-errors

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

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>

2 个答案:

答案 0 :(得分:8)

因为FooGetterIFoo<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 :(得分:1)

只是添加现有答案:这与使用的泛型无关。

考虑这个更简单的例子:

interface ISomething
{
}

class OtherThing
{
}

然后说(在方法内):

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

工作得很好。编译器不知道OtherThing是否可能是ISomething,所以当我们说它会成功时它会相信我们。但是,如果我们将OtherThing更改为密封类型(即sealed class OtherThing { }struct OtherThing { }),则不再允许投射。编译器知道它不能顺利(除非otnull,但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
}