2015-10-01 5 views
11

私はこの構造体があります。奇妙な変換演算子の挙動

public struct MyValue 
{ 
    public string FirstPart { get; private set; } 
    public string SecondPart { get; private set; } 

    public static implicit operator MyValue(string fromInput) 
    { // first breakpoint here. 
     var parts = fromInput.Split(new[] {'@'}); 
     return new MyValue(parts[0], parts[1]); 
    } 

    public static implicit operator string(MyValue fromInput) 
    { // second breakpoint here. 
     return fromInput.ToString(); 
    } 

    public override string ToString() 
    { 
     return FirstPart + "@" + SecondPart; 
    } 

    public MyValue(string firstPart, string secondPart) : this() 
    { 
     this.FirstPart = firstPart; 
     this.SecondPart = secondPart; 
    } 
} 

をそして、上記のコメントで示されるように、私はブレークポイントを設定しました。

は、それから私はこれを行う:

var first = new MyValue("first", "second"); 
if (first == (MyValue) null) throw new InvalidOperationException(); 

それはif (first == (MyValue) null)に入ったとき、私はいくつかの奇妙な行動を観察しています:第二ブレークポイントが何らかの理由でヒットします。単純な等価比較のためにMyValueを文字列に変換しようとしているのはなぜですか?私はコードを継続させる場合

そして、それは最初のブレークポイントにヒットし、今私はそれが文字列を変換しようとしている理由を思ったんだけど(値は、私は明示的にnullを投げてきたという事実にもかかわらずnullですMyValue)をMyValueに変換します。 if (first == (MyValue) null)のようなステートメントを使用するときは、文字列を使用しないでください。実際にここで何が起こっていますか?

+0

2つの暗黙的な演算子のうち、 'null 'を指定するとどちらが呼び出されると思われますか? – Igor

+0

どちらも、私は何も変換していないからです。しかし、私は明らかにそうです。したがって、私の質問です。 –

+0

これは '=='演算子の欠落したオーバーロードと関係しているかもしれません。 –

答えて

12

忙しいコメントとなり、問題の内容が明らかになりました。

C#コンパイラは(MyStruct) nullをコンパイルできませんが、あなたの場合にはコンパイルできません。

これは、nullが完全に有効な参照タイプ(このケースではstring)からの暗黙の演算子を持つ場合に発生します。

私はそれはあなたが:)

PSを見る方法を実行し、なぜあなたは今すぐに従うことができると思う:これは「非可逆」暗黙の演算子は、一般的には落胆している理由の良い例です。

+2

暗黙の変換は悪魔です。 –

+2

あなたはより単純な言葉でより多くをelabprateすることができます –

+0

これは、参照型を変換できる最初の(この場合は唯一の)暗黙の変換演算子を選択します。また、あなたは「C#コンパイラは(MyStruct)nullをコンパイルできませんが、あなたのケースではそうです」と言っています。 - なぜそれが何かを詳しく説明できますか? –

7

@leppies answerを完了するには、これは、呼び出し元のコード(リリースモード)である:

実際にこれにコンパイル
public void X() 
{ 
    var first = new MyValue("first", "second"); 
    if (first == (MyValue) null) throw new InvalidOperationException(); 
} 

public void X() 
{ 
    if (new MyValue("first", "second") == null) 
    { 
     throw new InvalidOperationException(); 
    } 
} 

そして、これは、呼び出しのために放出されたILです:

// Methods 
.method public hidebysig 
    instance void X() cil managed 
{ 
    // Method begins at RVA 0x20dc 
    // Code size 45 (0x2d) 
    .maxstack 8 

    IL_0000: ldstr "first" 
    IL_0005: ldstr "second" 
    IL_000a: newobj instance void MyValue::.ctor(string, string) 
    IL_000f: call string MyValue::op_Implicit(valuetype MyValue) 
    IL_0014: ldnull 
    IL_0015: call valuetype MyValue MyValue::op_Implicit(string) 
    IL_001a: call string MyValue::op_Implicit(valuetype MyValue) <--- This! 
    IL_001f: call bool [mscorlib]System.String::op_Equality(string, string) 
    IL_0024: brfalse.s IL_002c 
    IL_0026: newobj instance void [mscorlib]System.InvalidOperationException::.ctor() 
    IL_002b: throw 
    IL_002c: ret 
} // end of method C::X 

ご覧のとおり、という新しいインスタンスを作成した後の操作IL_001aは暗黙の変換をと呼びます。これはnullという値の型比較を実際にコンパイルするためのコンパイラの唯一の可能性であるです。

+2

C#で、これは: 'if((string)new MyValue(" first "、" second ")==(string)(MyValue)((string)null))throw InvalidOperationException (); ' –

+0

@LucasTrzesniewski正確ではありません。彼は明示的ではなく 'string'への'暗黙の 'キャストを宣言しています。 –

+1

はい、何が起こっているのかを明示するためにすべてを明示しました。 –