为什么在将类转换为未实现的接口时没有编译器错误?

McG*_*gle 10 c# abstract-class casting interface c#-4.0

如果我尝试从类到接口的无效转换,那么编译器不会抱怨(错误发生在运行时); 但是,如果我尝试对抽象类进行类似的转换,它确实会抱怨.

class Program
{
    abstract class aBaz
    {
        public abstract int A { get; }
    }

    interface IBar
    {
        int B { get; }
    }

    class Foo
    {
        public int C { get; }
    }

    static void Main()
    {
        Foo foo = new Foo();

        // compiler error, as expected, since Foo doesn't inherit aBaz
        aBaz baz = (aBaz)foo;

        // no compiler error, even though Foo doesn't implement IBar
        IBar bar = (IBar)foo;
    }
}
Run Code Online (Sandbox Code Playgroud)

为什么编译器拒绝从FooIBar的转换,当它(似乎?)无效时?或者,为了解决问题,如果编译器允许对接口IBar进行 "无效" 转换,为什么它不允许对抽象类aBaz进行类似的"无效" 转换

Bit*_*Man 8

你需要了解.Net的继承系统,看看为什么这是有意义的.在.Net中,类可以仅从一个基类继承,但可以实现任意数量的接口.

class Program
{
    abstract class aBaz
    {
        public abstract int A { get; }
    }

    interface IBar
    {
        int B { get; }
    }

    class Foo
    {
        public int C { get; }
    }

    class BarableFoo : Foo, IBar
    {
        public int C { get; }
    }

    static void Main()
    {
        // This is why the compiler doesn't error on the later cast
        Foo foo = new BarableFoo();

        // compiler error: aBaz is a class and the compiler knows that
        // Foo is not a _subclass_ of aBaz.
        aBaz baz = (aBaz)foo;

        // no compiler error: the class Foo does not implement IBar, however at runtime
        // this instance, "foo", might be a subclass of Foo that _implements_ IBar.
        // This is perfectly valid, and succeeds at runtime.
        IBar bar = (IBar)foo;

        // On the other hand...
        foo = new Foo();

        // This fails at runtime as expected. 
        bar = (IBar)foo;
    }

}
Run Code Online (Sandbox Code Playgroud)

在这个问题非常简单原始的例子,它看起来像编译器可以检测到FOO的这个实例是永远不会被强制转换为伊巴尔,但更多的是"锦上添花",警告不是语言正确性的问题.

  • +1。还添加了答案,显示编译时强制转换失败的情况。 (2认同)

SLa*_*aks 6

强制转换的重点是抑制编译器错误.
(例如,如果您知道这foo实际上是实现接口的子类型的实例)

如果编译器可以证明演员阵容无法成功,那么它仍然会出错.(例如,如果你投的不在其层次结构)

  • @McGarnagle:“Foo”的后代之一必须实现“IBar”才能使转换成为可能,但在许多情况下,编译器无法知道包含实现“IBar”的“Foo”的某些后代的程序集` 可能会被加载。 (2认同)

Ale*_*kov 5

并且为了表明编译器不是愚蠢的,当编译时编译失败时会出现这样的情况:如果编译器可以证明没有其他类可以从类派生,它将无法在编译时转换为接口:

sealed class NoBar
{ 
} 

struct NoBarValue
{
}

IBar noBar = (IBar)(new NoBar()); // fails at compile time
IBar noBarValue = (IBar)(new NoBarValue()); // fails at compile time
Run Code Online (Sandbox Code Playgroud)

在第一种情况下(NoBar)类是显式密封的(因此没有派生类可以实现IFoo)并且编译器知道它没有实现IBar自身 - 因此在编译时可能会失败.第二种情况(NoBarValue)类似,唯一的区别是值类型(结构)被隐式密封.