14

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

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;
    }
}

为什么编译器不拒绝从FooIBar的转换,当它(看似?)无效时?或者,换个问题,如果编译器允许对接口 IBar 进行这种“无效”强制转换,为什么它不允许对抽象类aBaz进行类似的“无效”强制转换?

4

3 回答 3

11

您需要了解 .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;
    }

}

在问题中极其简单的原始示例中,编译器似乎可以检测到 foo 的这个实例永远不会被转换为 IBar,但这更像是一个“很高兴”的警告,而不是语言正确性的问题。

于 2012-09-09T01:42:42.043 回答
7

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

如果编译器可以证明强制转换不可能成功,它仍然会报错。(例如,如果您转换为不在其层次结构中的类)

于 2012-09-09T01:12:28.970 回答
6

并且为了表明编译器并不愚蠢,实际上存在强制转换在编译时失败的情况:如果编译器可以证明没有其他类可以从该类派生,它将在编译时无法转换为接口:

sealed class NoBar
{ 
} 

struct NoBarValue
{
}

IBar noBar = (IBar)(new NoBar()); // fails at compile time
IBar noBarValue = (IBar)(new NoBarValue()); // fails at compile time

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

于 2012-09-09T03:57:30.140 回答