2015-10-01 12 views
11

Ich habe diese Struktur:Seltsam Konvertierungsoperator Verhalten

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; 
    } 
} 

Und ich habe Haltepunkte setzen, wie oben durch die Kommentare angezeigt.

Dann mache ich das:

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

Ich bin ein seltsames Verhalten beobachtet, wenn es if (first == (MyValue) null) eintritt: der zweite Haltepunkt aus irgendeinem Grund getroffen wird. Warum versucht es die MyValue in eine Zeichenfolge für einen einfachen Gleichheitsvergleich zu konvertieren?

Dann, wenn ich den Code lassen fortsetzen, den ersten Haltepunkt trifft, und jetzt frage ich mich, warum ist es, eine Zeichenfolge zu konvertieren versuchen (der Wert ist null trotz der Tatsache, dass ich ausdrücklich null in ein geworfen haben MyValue) in eine MyValue? Strings sollten nicht mit einbezogen werden, wenn eine Aussage wie if (first == (MyValue) null) verwendet wird, was passiert hier eigentlich?

+0

Welcher der beiden impliziten Operatoren wird erwartet, wenn Sie 'null' liefern? – Igor

+0

Weder, weil ich nichts "umwandle". Aber anscheinend bin ich. Daher meine Frage. –

+0

Vielleicht hat es etwas mit der fehlenden Überladung für den Operator '== zu tun. –

Antwort

12

War beschäftigt, kommentieren und es wurde klar, was das Problem ist.

Der C# -Compiler kann (MyStruct) null nicht kompilieren, aber in Ihrem Fall tut es.

Dies passiert, weil Sie einen impliziten Operator von einem Referenztyp haben (dieser Fall string), wobei null vollkommen gültig ist.

Ich glaube, Sie können nun folgen, warum es die Art und Weise führt Sie :)

PS sehen: Dies ist ein gutes Beispiel dafür ist, warum ‚verlustbehaftete‘ implizite Operatoren im Allgemeinen abgeraten.

+2

Implizite Konvertierungen sind der Teufel. –

+2

können Sie mehr in einfacheren Worten –

+0

elabprate So wählt es nur den ersten (und in diesem Fall nur) impliziten Konvertierungsoperator findet es, dass ein Referenztyp konvertieren kann? Sie sagen auch: "Der C# -Compiler kann nicht kompilieren (MyStruct) null, aber in Ihrem Fall tut es." - Können Sie näher erläutern, warum das so ist? –

7

@leppies answer abzuschließen, ist dies die Telefonvorwahl (Release-Modus):

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

Welche dies kompiliert, um tatsächlich:

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

und dies ist die IL für den Anruf emittiert:

// 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 

Wie Sie sehen können, nachdem Sie die neue Instanzerstellen, Operation IL_001a ruft die implizite Konvertierung in string auf, da die einzige Möglichkeit für den Compiler ist, den Vergleich des Wertetyps mit null tatsächlich zu kompilieren.

+2

In C# ist dies: 'if ((string) new MyValue (" erste "," zweite ") == (string) (MyValue) ((string) null)) neue InvalidOperationException werfen(); ' –

+0

@ LucasTrzesniewski Nicht genau. Er deklariert eine "implizite" Besetzung zu "String", keine explizite. –

+1

Ja, ich habe einfach alles explizit gemacht, um zu zeigen, was vor sich geht. –