当`x as Y`工作正常时,为什么这个演员表无效?

ver*_*ald 5 c# casting

我昨天偶然发现了这个奇怪的情况,其中t as D返回一个非空值,但(D)t会导致编译器错误.

由于我匆忙,我只是使用t as D和继续,但我很好奇为什么演员阵容无效,因为t真的是一个D.任何人都可以解释为什么编译器不喜欢演员?

class Program
{
    public class B<T> where T : B<T> { }

    public class D : B<D> { public void M() { Console.Out.WriteLine("D.M called."); } }

    static void Main() { M(new D()); }

    public static void M<T>(T t) where T : B<T>
    {
        // Works as expected: prints "D.M called."
        var d = t as D;
        if (d != null)
            d.M();

        // Compiler error: "Cannot cast expression of type 'T' to type 'D'."
        // even though t really is a D!
        if (t is D)
            ((D)t).M();
    }
}
Run Code Online (Sandbox Code Playgroud)

编辑:玩,我认为这是一个更清晰的例子.在这两种情况下都t被限制为a B并且可能是a D.但是泛型的情况不会编译.在确定演员表是否合法时,C#是否会忽略通用约束?即使它确实忽略它,t仍然可能是一个D; 那么为什么这是编译时错误而不是运行时异常呢?

class Program2
{
    public class B { }

    public class D : B { public void M() { } }

    static void Main()
    {
        M(new D());
    }

    public static void M(B t)
    {
        // Works fine!
        if (t is D)
            ((D)t).M();
    }

    public static void M<T>(T t) where T : B
    {
        // Compile error!
        if (t is D)
            ((D)t).M();
    }
}
Run Code Online (Sandbox Code Playgroud)

Dav*_*eau 3

在你的第二个例子中你可以改变

((D)t).M();
Run Code Online (Sandbox Code Playgroud)

((D)((B)t)).M();
Run Code Online (Sandbox Code Playgroud)

您可以将 from 转换为Bto D,但不一定能将“某物是B”转换为 to D。“某物是一个B”可以是一个A,例如,if A : B

当您可能在层次结构中从一个子项跳转到另一个子项时,就会出现编译器错误。您可以在层次结构中上下投射,但不能跨层次投射。

((D)t).M();       // potentially across, if t is an A
((D)((B)t)).M();  // first up the hierarchy, then back down
Run Code Online (Sandbox Code Playgroud)

另请注意,您可能仍然会收到运行时错误((D)((B)t)).M();if tis not 事实上 a D。(is不过你的检查应该可以防止这种情况发生。)

(另请注意,在这两种情况下,编译器都不会考虑检查if (t is D)。)

最后一个例子:

class Base { }
class A : Base { }
class C : Base { }

...
A a = new A();
C c1 = (C)a;         // compiler error
C c2 = (C)((Base)a); // no compiler error, but a runtime error (and a resharper warning)

// the same is true for 'as'
C c3 = a as C;           // compiler error
C c4 = (a as Base) as C; // no compiler error, but always evaluates to null (and a resharper warning)
Run Code Online (Sandbox Code Playgroud)