5

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

由于我很着急,所以我只是使用并继续,但我很t as D好奇为什么演员表无效,因为. 谁能解释为什么编译器不喜欢演员表?tD

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

编辑:到处玩,我认为这是一个更清楚的例子。在这两种情况下t都被限制为 aB并且可能是 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();
    }
}
4

3 回答 3

3

在您的第二个示例中,您可以更改

((D)t).M();

((D)((B)t)).M();

您可以从Bto投射D,但不一定从“something that is a B”投射到D。“Something that is a B” 可以是A,例如,如果A : B

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

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

另请注意,((D)((B)t)).M();如果t实际上不是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)
于 2012-05-25T03:32:28.757 回答
1

将其更改为

public static void M<T>(T t) where T : D

如果您想强制 T 必须是 D 类型,这是适当的限制。

如果您的限制将 T 定义为 T 的 D,它将不会编译。

例如:你不能施放List<int> to int,反之亦然

于 2012-05-25T00:35:10.487 回答
0

您的模板函数有一个约束,T需要B<T>.

因此,当您的编译器尝试将t类型对象转换T为它时,D它无法做到。因为T是有保证的B<T>,但不是D

如果您将约束添加到 require that Tis D,这将起作用。iewhere T: B<T>, D或者简单地说,这也where T: D 保证了,因为继承链。TB<T>

问题的第二部分:当你调用t as D它时,它会在运行时进行检查。并且,在运行时,使用多态性,验证t可以转换为D类型,并且没有错误。

注意:顺便说一句,这段代码太奇怪了。你确定你在做什么?

于 2012-05-25T00:35:32.950 回答