アップデート!
以下の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
Type1
Type0
Type1
Type2
該当するユーザー定義の変換演算子Uのセットを見つけます。このセットは、Sを含む型からTを含む型に変換する、Dのクラスまたは構造体によって宣言されたユーザー定義の暗黙的な変換演算子で構成されます。Uが空の場合、変換は未定義であり、コンパイル時エラーが発生します。
さて、これらの条件を満たす2つの演算子があります。で宣言されたバージョンは、がDであり、(明らかにSを含む)から(明らかにTを含む)に変換されるType1
ため、要件を満たしています。のバージョンも、まったく同じ理由で要件を満たしています。したがって、Uにはこれらの演算子の両方が含まれます。Type1
Type2
Type1
Type2
最後に、 Uの演算子の最も具体的な「ソースタイプ」SXを見つけることに関して:
Uの演算子のいずれかがSから変換される場合、SXはSです。
これで、Uの両方の演算子がSから変換されます。これにより、 SXがSであることがわかります。
Type2
これは、そのバージョンを使用する必要があるという意味ではありませんか?
ちょっと待って!よくわかりません!
演算子のバージョンのみを定義することはできませんでしType1
た。その場合、残りの候補はType1
のバージョンのみになりますが、仕様によればSXは次のようになりType2
ますか?これは、仕様で不可能なことが義務付けられている可能性のあるシナリオのようです(つまり、で宣言された変換はType2
、実際には存在しない場合に使用する必要があります)。