6

アップデート!

以下のC#仕様の一部の私の分析を参照してください。この質問で説明している動作は実際には仕様に違反しているように見えるので、何かが足りないに違いないと思います。

アップデート2!

OK、さらに考えてみると、いくつかのコメントに基づいて、私は今何が起こっているのか理解していると思います。仕様の「ソースタイプ」という言葉は、変換元のタイプを指します。つまり、Type2以下の例では、コンパイラが候補を定義された2つの演算子に絞り込むことができることを意味します(Type2ソースタイプであるため)。両方のための)。ただし、これ以上選択肢を絞り込むことはできません。したがって、仕様のキーワード(この質問に適用される)は「ソースタイプ」です。これは、以前は「タイプの宣言」を意味すると誤解していました(私は思います)。


元の質問

これらのタイプを定義したとします。

class Type0
{
    public string Value { get; private set; }

    public Type0(string value)
    {
        Value = value;
    }
}

class Type1 : Type0
{
    public Type1(string value) : base(value) { }

    public static implicit operator Type1(Type2 other)
    {
        return new Type1("Converted using Type1's operator.");
    }
}

class Type2 : Type0
{
    public Type2(string value) : base(value) { }

    public static implicit operator Type1(Type2 other)
    {
        return new Type1("Converted using Type2's operator.");
    }
}

次に、私がこれを行うと言います:

Type2 t2 = new Type2("B");
Type1 t1 = t2;

implicitどの演算子を使用すべきかが明確でないため、これは明らかにあいまいです。私の質問は-このあいまいさを解決する方法がわからないので(どのバージョンが必要かを明確にするために明示的なキャストを実行できるわけではありません)、それでも上記のクラス定義はコンパイルされます-なぜコンパイラは許可するのでしょうかそれらの一致するimplicit演算子はまったく?


解剖

OK、これを理解するために、Hans Passantが引用したC#仕様の抜粋を見ていきます。

ユーザー定義の変換演算子が考慮されるタイプのセットDを見つけます。このセットは、S(Sがクラスまたは構造体の場合)、Sの基本クラス(Sがクラスの場合)、およびT(Tがクラスまたは構造体の場合)で構成されます。

SからT )変換しています。したがって、ここでDには、例の3つのタイプすべてが含まれているようです:(これはSの基本クラスであるため)、(T)、および(S)。Type2 Type1Type0Type1Type2

該当するユーザー定義の変換演算子Uのセットを見つけます。このセットは、Sを含む型からTを含む型に変換する、Dのクラスまたは構造体によって宣言されたユーザー定義の暗黙的な変換演算子で構成されます。Uが空の場合、変換は未定義であり、コンパイル時エラーが発生します。

さて、これらの条件を満たす2つの演算子があります。で宣言されたバージョンは、がDであり、(明らかにSを含む)から(明らかにTを含む)に変換されるType1ため、要件を満たしています。のバージョン、まったく同じ理由で要件を満たしています。したがって、Uにはこれらの演算子の両方が含まれます。Type1Type2Type1Type2

最後に、 Uの演算子の最も具体的な「ソースタイプ」SXを見つけることに関して:

Uの演算子のいずれかがSから変換される場合、SXはSです。

これで、Uの両方の演算子がSから変換されます。これにより、 SXSであることがわかります。

Type2これは、そのバージョンを使用する必要があるという意味ではありませんか?

ちょっと待って!よくわかりません!

演算子のバージョンのみを定義することはできませんでしType1た。その場合、残りの候補はType1のバージョンのみになりますが、仕様によればSXは次のようになりType2ますか?これは、仕様で不可能なことが義務付けられている可能性のあるシナリオのようです(つまり、で宣言された変換はType2、実際には存在しない場合に使用する必要があります)。

4

2 に答える 2

2

最終的に、それは完全な成功で禁止することはできません。あなたと私は2つのアセンブリを公開できます。それらは、私たち自身を更新しながら、お互いのアセンブルを使い始めることができました。次に、各アセンブリで定義されたタイプ間に暗黙のキャストをそれぞれ提供できます。次のバージョンをリリースするときにのみ、コンパイル時ではなく、これをキャッチできます。

禁止できないものを禁止しようとしないことには利点があります。それは明確さと一貫性をもたらすからです(そしてその中には立法者のための教訓があります)。

于 2010-08-24T23:52:57.627 に答える
1

あいまいさを引き起こす可能性のある変換を定義するためだけに、コンパイル時のエラーになることは望ましくありません。Type0を変更してdoubleを格納し、何らかの理由で符号付き整数と符号なし整数に別々に変換したいとします。

class Type0
{
    public double Value { get; private set; }

    public Type0(double value)
    {
        Value = value;
    }

    public static implicit operator Int32(Type0 other)
    {
        return (Int32)other.Value;
    }

    public static implicit operator UInt32(Type0 other)
    {
        return (UInt32)Math.Abs(other.Value);
    }

}

これは正常にコンパイルされ、両方の変換を使用できます

Type0 t = new Type0(0.9);
int i = t;
UInt32 u = t;

float f = tただし、暗黙の変換のいずれかを使用して整数型を取得し、それをfloatに変換できるため、コンパイルエラーが発生します。

上記のType0をコンパイルしたいので、実際に使用されたときに、コンパイラーがこれらのより複雑なあいまいさについて文句を言うことだけが必要です。一貫性を保つために、あいまいさを単純化すると、定義するときではなく、使用するときにエラーが発生するはずです。

編集

ハンスは仕様を引用した彼の答えを削除したので、変換があいまいであるかどうかを判断するC#仕様の部分を簡単に説明し、Uをすべての変換のセットとして定義しました。

  • Uの演算子の最も具体的なソースタイプSXを見つけます。
    • Uの演算子のいずれかがSから変換される場合、SXはSです。
    • それ以外の場合、SXは、Uの演算子のターゲットタイプの組み合わせセットの中で最も包括的タイプです。最も包括的タイプが見つからない場合、変換はあいまいであり、コンパイル時エラーが発生します。

言い換えると、Sから直接変換する変換を好みます。そうでない場合は、Sを「最も簡単に」変換できるタイプを好みます。どちらの例でも、Sからの2つの変換が利用可能です。からの変換がなかった場合は、からのType2変換を優先Type0objectます。変換するのに明らかに良い選択が1つもない場合は、ここで失敗します。

  • Uの演算子の最も具体的なターゲットタイプTXを見つけます。
    • Uの演算子のいずれかがTに変換される場合、TXはTです。
    • それ以外の場合、TXは、Uの演算子のターゲットタイプの組み合わせセットの中で最も包括的なタイプです。最も包括的なタイプが見つからない場合、変換はあいまいであり、コンパイル時エラーが発生します。

繰り返しになりますが、Tに直接変換することをお勧めしますが、Tに変換するのが「最も簡単」なタイプを選択します。Danの例では、Tへの変換が2つあります。私の例では、可能なターゲットはとでInt32ありUInt32、どちらも他よりもよく一致しないため、ここで変換が失敗します。コンパイラには、float f = tmeansfloat f = (float)(Int32)tか。かを知る方法がありませんfloat f = (float)(UInt32)t

  • UにSXからTXに変換するユーザー定義の変換演算子が1つだけ含まれている場合、これが最も具体的な変換演算子です。そのような演算子が存在しない場合、またはそのような演算子が複数存在する場合、変換はあいまいであり、コンパイル時エラーが発生します。

ダンの例では、SXからTXへの変換が2つ残っているため、ここでは失敗します。SXとTXを決定するときに異なる変換を選択した場合、SXからTXへの変換はありません。たとえば、Type1aから派生した場合、からへの変換とからへのType1変換がある可能性があります。これらはSX=Type2とTX=Type1を提供しますが、実際にはType2からType1への変換はありません。これは本当にあいまいなので、これは問題ありません。コンパイラーは、Type2をType1aに変換してからType1にキャストするか、最初にType0にキャストして、その変換をType1に使用できるようにするかを認識していません。Type2Type1aType0Type1

于 2010-08-25T00:12:51.073 に答える