5

私は昨日この奇妙なケースに出くわしました。そこでt as Dはnull以外の値が返されます(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();
    }
}

編集:遊んで、これはより明確な例だと思います。どちらの場合もt、に制約され、Bおそらくは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

2番目の例では、変更できます

((D)t).M();

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

からBにキャストDできますが、必ずしも「あるもの」から にキャストできるとは限りませBD。「あるもの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

ifが実際には ではない((D)((B)t)).M();場合でも実行時エラーが発生する可能性があることにも注意してください。(あなたのチェックはこれを防ぐべきです。)tDis

(どちらの場合も、コンパイラはチェックを考慮していないことに注意して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

を必要とする制約を追加する TD、これは機能します。つまり、継承チェーンのために、それ も保証しwhere T: B<T>, Dます。where T: DTB<T>

質問の 2 番目の部分: 呼び出すt as Dと、実行時にチェックされます。そして、実行時にポリモーフィズムを使用して、型tに変換できることが検証されD、エラーなしで変換されます。

注: ところで、このコードはとても奇妙です。あなたは何をしているのか確信がありますか?

于 2012-05-25T00:35:32.950 に答える