8

次のコードに問題があります。オブジェクトに文字列を明示したいのですが、これは完全に正常に機能していますが、このオブジェクトがジェネリック クラスの一部である場合、次のエラー例外で失敗します:「タイプ 'System.String' のオブジェクトをキャストできません」 「test.B」と入力します」。メソッドをオーバーロードしましたが。

using System;
using System.Collections.Generic;

namespace test {
    class Program {
        static void Main(string [] args) {
            // These two cast perfectly fine.
            B x = (B) "abc";
            C y = (C) "def";

            A <B> a = new A<B>();
            a.b();
            A <C> b = new A<C>();
            b.b();
        }
    }

    class A<T> {
        public List <T> a = new List<T>();

        public void b() {
            // Unable to cast object of type 'System.String' to type 'test.B'
            this.a.Add ((T) (object) "abc"); 
            this.a.Add ((T) (object) "def");
            this.a.Add ((T) (object) "ghi");
        }
    }

    class B {
        public string b;

        public static explicit operator B(string a) {
            B x = new B();
            x.b = a;
            return x;
        }
    }

    class C {
        public string c;

        public static explicit operator C(string a) {
            C x = new C();
            x.c = a;
            return x;
        }
    }
}

これが適切にキャストされない理由を誰かが説明してくれたら最高です。

ありがとう

4

4 に答える 4

12

変換演算子は、タイプが静的にわかっている場合にのみ適用されます。結局のところ、汎用メソッドはすべてにまったく同じILを使用する必要があるTため、場合によってはオペレーターを呼び出すことができず、他の場合は型チェックを行うことができません。

さらに、明示的ににキャストしているためobject、使用されることはありません。からのキャストobjectは、常に単純なボックス解除またはタイプチェックです。

邪悪な修正は次のようになります(そして私はこれが好きではありません):

        this.a.Add((T)(dynamic)"abc");
        this.a.Add((T)(dynamic)"def");
        this.a.Add((T)(dynamic)"ghi");

これは、解決を実行時に延期します。動作しますが、その後は目を洗う必要があります。ただし、より一般的には、演算子とジェネリックはうまく機能しません。したがって、APIでその組み合わせを使用しないようにしてください。私は個人的に上記を実際には使用しません!

于 2012-08-13T12:19:44.187 に答える
2

ジェネリックパラメーターに使用している明示的なキャストがある場合、コンパイラーはコンパイル時に知識を持ちません。

ただし、インターフェースを導入し、ジェネリックパラメーターに制約を課すことができます

于 2012-08-13T12:20:49.897 に答える
2

(オブジェクト)キャストを挿入して、コンパイラが間違っていることを伝えないようにしました。それは機能し、コンパイラは型をチェックできなくなったため、文句を言うことができなくなりました。また、オブジェクトから T へのキャストは、可能性が低くても実際に機能する可能性があります。

ただし、期待していなかったのは、明示的な変換演算子が C# 言語の機能であることです。このようなキャストが適用されたときに実行するメソッドを知っているのはコンパイラだけです。これはCLR の機能ではありません。ランタイムは、変換を行うための適切なメソッドを見つけようと探したり収集したりしません。それ以外の場合、C# コンパイラは、コンパイル時に指定した演算子を使用することができず、T の型を認識しません。

問題は、キャストの実行時にコンパイラが役に立たなくなったことです。カブーム。

コンパイル時ではなく実行時にジェネリック型を適用することは .NET の機能であり、ジェネリックという用語は「具体化されたジェネリック」です。「型消去」とは対照的に、ジェネリックが Java および C++ で実装される方法。型消去の問題は、コードがコンパイルされた後にジェネリック型引数に関するすべての情報が失われ、ジェネリック型が別の言語で使用できず、リフレクションが機能しないことです。具体化されたジェネリックの問題は、どの型にも普遍的に適用できない操作を使用できないことです。operator+() のように。そしてこんなキャスト。

于 2012-08-13T12:42:28.613 に答える
1

forは、またはの明示的なキャスト演算子について知らないためTです。A<T>BC

于 2012-08-13T12:19:08.167 に答える