4

C#では、「同じ」ユーザー定義の変換が2回存在する場合に、コンパイルエラーが発生しないのはなぜですか。(ソースクラスに1回、ターゲットクラスに1回?)

たとえば、次のコードをコンパイルしようとしても、コンパイルエラーは発生しません。

namespace TestConversionOverloading
{
    public class A
    {
        public int value = 1;

        public static explicit operator B(A a)
        {
            B b = new B();

            b.value = a.value + 6;

            return b;
        }
    }

    public class B
    {
        public int value = 2;

        public static explicit operator B(A a)
        {
            B newB = new B();

            newB.value = a.value;

            return newB;
        }
    }

    public class program
    {
        public static void Main() {}
    }
}

ただし、AをBに明示的に変換しようとするとコンパイルエラーが発生します。Main()に以下を追加して、コンパイルしようとします。

A a = new A();
B b = ((B)a);

私は以下を取得します:

「TestConversionOverloading.A」から「TestConversionOverloading.B」に変換するときのあいまいなユーザー定義の変換 「TestConversionOverloading.A.explicit演算子TestConversionOverloading.B(TestConversionOverloading.A)」
および 「TestConversionOverloading.B.explicit演算子TestConversionOverloading.B(TestConversionOverloading.A)」 。 '

では、定義から直接エラーを出してみませんか?どちらかの変換を使用する方法はありますか?

4

4 に答える 4

3

言語がこれを許可することがなぜ理にかなっているのかについては推測しませんが、両方のクラスを制御している場合、明らかな解決策は演算子の1つを取り除くことです。

できない場合は、リフレクションを使用して曖昧さを解消する方法を次に示します。

まず、目的の演算子にバインドするデリゲートを作成します。

// Picks the conversion operator declared in class A.
var method = typeof(A).GetMethod("op_Explicit", new[] { typeof(A) });
var converter = (Func<A, B>)Delegate.CreateDelegate(typeof(Func<A, B>), method);

次に、デリゲートを次のように使用します。

A a = ...
B b = converter(a);
于 2011-02-23T00:48:53.100 に答える
2

仕様によると、これは予想される動作です。

元のテキストを大幅に圧縮すると、この場合は次のようになります。コンパイラは、両方のクラス定義で A を B に変換できるすべての演算子を見つけます。これにより、 と が参加A operator B(A a)B operator B(A a)ます。それで、

そのような演算子が存在しない場合、またはそのような演算子が複数存在する場合、変換があいまいになり、コンパイル時エラーが発生します。

では、なぜ定義から直接エラーを出さないのでしょうか? どちらの定義も問題ありませんが、問題が発生するのはそれらの使用です。

どちらかの変換を使用する方法はありますか? これを行う簡単な方法がわかりません。コンパイラをバイパスして、IL を手動で発行することを考えています。そうすれば、プログラムにいずれかの演算子を使用するように指示できると思います。ただし、これが完全に実行可能かどうかはわかりません。Reflector のようなツールが役立ちます。

演算子ベースの変換を使用することにはいくつかの利点がありますが、いずれかのクラスで 1 つの演算子が失われるか、コンストラクター ベースの変換またはより単純なToA(A a)andの構文に変更できますFromA(A a)。あるいは、エリック・リッパートが言葉の巧妙さで私たちを啓発してくれるかもしれません!

于 2011-02-23T00:16:32.587 に答える
1

「public static implicit operator B(A a)」コード行ごとに生成された IL コードを見てください。

.method public hidebysig specialname static
class TestConversionOverloading.B  op_Explicit(class TestConversionOverloading.A a) cil managed

最初の質問に対する答えは次のとおりです。暗黙的/明示的な変換演算子は構文糖衣です。MSIL では、それらは通常の方法のように見えます (実際にそうです)。2 つの異なるクラスが同一の署名を持つメソッドを持っていても、何にも違反していないため、犯罪にはなりません。ただし、この場合、変換演算子の呼び出しはコンパイルできません。前述のように、リフレクションを使用していずれかのメソッドの MethodInfo を取得できます。

于 2011-09-16T12:41:20.463 に答える
0

競合する変換の 1 つがジェネリックである可能性があり、ジェネリック パラメーターの他の組み合わせに役立つ可能性があることに注意してください。

SAME クラスで競合する変換を定義することもできます。

class C<T>
{
    implicit operator int() { return 0; }
    implicit operator T() { return default(T); }
}

C<int> c;
int i = c;

コンパイラがこれについて文句を言うと、 がと のC<string>両方に変換できなくstringなりintます。

于 2011-02-23T01:42:25.183 に答える