2016-07-05 23 views
33

Sono nuovo di C# ed esplorare le caratteristiche del linguaggio, mi sono imbattuto in qualcosa di strano:Perché C# richiede parentesi quando si utilizza nullable in un'espressione?

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

Qualcuno potrebbe spiegare a me perché sono necessari la parentesi?

+1

Nel primo caso si sta cercando di accedere a un membro di nome 'Value' in' foo', che non esiste. Nella seconda affermazione, "Valore" si riferisce a una proprietà di "Nullable ". – xfx

+9

Se ci pensate davvero, chiamare ".Value" su un'espressione che include l'operatore condizionale nullo è contraddittorio (o aspettatevi un nulla o non lo fate).Molto probabilmente vorrai usare un operatore a coalescenza nulla, nel qual caso le parentesi non sono necessarie. ad es .: 'Foo foo2 = foo? .Identity() ?? '; – sstan

risposta

41

Qualcuno potrebbe spiegarmi perché sono necessarie le parentesi?

perché Identity() rendimenti ha una Foo (non un Foo?) e, quindi, alcuna proprietà Value. Se foo è nullo, il null si propagherà attraverso la chiamata Identity.

Quando si inserisce parentesi intorno ad esso, i risultati del espressione è un Nullable<Foo> che fa dispongono di una proprietà Value.

Si noti inoltre che se fooè nulla, allora si sarà chiamando Value su un Nullable<Foo> che non ha alcun valore, e otterrà un'eccezione a run-time. Alcuni analizzatori statici riconosceranno che è possibile che si verifichi un'eccezione di riferimento null in attesa di avverarsi.

Se li si espande ai loro equivalenti, senza null-propagazione sarà più chiaro:

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 

If Identity() rendimenti Foo, perché Foo foo3 = foo?.Identity(); si compila?

L'equivalente di ciò sarebbe:

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

Le gioie di usare lo stesso simbolo per indicare diversi operatori. – BoltClock

+0

Hai vinto contro il mio computer e rete molto lenti :) –

+0

Se 'Identity()' restituisce 'Foo', perché' Foo foo3 = foo? .Identity(); 'non compila? – Lukas92

0

Credo che sia stata una buona decisione da parte del team C# per farlo in questo modo. Si consideri il seguente scenario:

Se lo struct era:

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

Se non ha bisogno della parentesi per accedere al risultato Nullable, non sarebbe in grado di accedere alla proprietà ID. Dal momento che il seguito non verrà compilato:

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

Quando si scrive foo?.Identity(). ciò che è dopo la . è di tipo Foo restituito da Identity(). Tuttavia, in (foo?.Identity()). ciò che è dopo la . è Foo? che è il risultato effettivo di tutta la dichiarazione foo?.Identity().

Problemi correlati