2016-07-05 83 views
33

Ich bin neu in C# und während die Sprachfunktionen zu erkunden, stieß ich auf etwas Seltsames:Warum benötigt C# Klammern, wenn NULL in einem Ausdruck verwendet wird?

struct Foo 
{ 
    public Foo Identity() { return this; } 

    public static void Bar(Foo? foo) 
    { 
     Foo foo1 = foo?.Identity().Value; // Does not compile 
     Foo foo2 = (foo?.Identity()).Value; // Compiles 
    } 
} 

jemand mir erklären könnte, warum die Klammer benötigt?

+1

Im ersten Fall versuchen Sie, ein Mitglied für den Zugriff auf Namen 'Value' in 'foo', die es nicht gibt. In der zweiten Anweisung bezieht sich "Wert" auf eine Eigenschaft von "Nullable ". – xfx

+9

Wenn Sie wirklich darüber nachdenken, ist das Aufrufen von '.Value' in einem Ausdruck, der den bedingten Null-Operator enthält, widersprüchlich (Sie erwarten entweder eine Null oder Sie nicht).Sie würden höchstwahrscheinlich stattdessen einen Null-Koaleszenzoperator verwenden, in welchem ​​Fall die Klammern nicht benötigt werden. z.B .: 'Foo foo2 = foo? .Identität() ?? '; – sstan

Antwort

41

Könnte mir jemand erklären, warum die Klammern benötigt werden?

Da Identity() kehrt ein Foo (kein Foo?) und hat somit keine Value Eigenschaft. Wenn foo null ist, wird der Nullwert über den Identity-Aufruf weitergegeben.

Wenn Sie Klammern um es ausdrückte, die Ergebnisse der Ausdruck ist ein Nullable<Foo> die tut eine Value Eigenschaft.

Beachten Sie auch, dass, wenn foo null ist, dann werden Sie Value auf einem Nullable<Foo> Aufruf sein, die keinen Wert hat, und wird eine Ausnahme zur Laufzeit bekommen. Einige statische Analysatoren erkennen, dass eine mögliche Null-Referenz-Ausnahme auf Sie wartet und Sie warnt.

Wenn man sie ohne Null-Ausbreitung auf ihre Äquivalente erweitern es klar sein wird:

Foo foo1; 
if(foo != null) 
{ 
    foo1 = foo.Identity().Value; // not possible - Foo has no Value property. 
} 
else 
{ 
    foo1 = null; // also not possible 
} 

Foo foo2; 
Foo? temp; 
if(foo != null) 
{ 
    temp = foo.Identity(); 
} 
else 
{ 
    temp = null; // actually a Nullable<Foo> with no value 
} 
foo2 = temp.Value; // legal, but will throw an exception at run-time if foo is null 

Wenn Identity() kehrt Foo, warum Foo foo3 = foo?.Identity(); kompilieren nicht?

Die äquivalent, das wäre:

Foo foo3 
if(foo != null) 
{ 
    foo3 = foo.Identity(); 
} 
else 
{ 
    foo3 = null; // not possible 
} 
+0

Das Vergnügen, das gleiche Symbol zu verwenden, um verschiedene Operatoren zu meinen. – BoltClock

+0

Sie haben gegen meinen sehr langsamen Computer und Netzwerk gewonnen :) –

+0

Wenn 'Identity()' 'Foo' zurückgibt, warum' 'Foo foo3 = foo? .Identity();' nicht kompilieren? – Lukas92

0

Ich denke, dass es eine gute Entscheidung von dem C# Team ist es auf diese Weise zu tun. Betrachten Sie das folgende Szenario:

Wenn die Struktur war:

struct Foo 
{ 
    public int ID { set; get; } 

    public Foo Identity() { return this; } 

    public static void Bar(Foo? foo) 
    { 
     int? foo1 = foo?.Identity().ID; // compile 
     Foo foo2 = (foo?.Identity()).Value; // Compiles 
    } 
} 

Wenn Sie die Klammer brauchte das Nullable Ergebnis zuzugreifen, würden Sie nicht in der Lage sein, die ID Eigenschaft zuzugreifen. Da die unten nicht kompiliert:

int? foo2 = (foo?.Identity()).GetValueOrDefault()?.ID 

Wenn Sie foo?.Identity(). schreiben, was ist nach dem . vom Typ Foo von Identity() zurückgegeben. Doch in (foo?.Identity())., was nach den . ist Foo?, die das tatsächliche Ergebnis der gesamten Aussage ist foo?.Identity().