2016-07-05 36 views
33

Jestem nowym do C# i badając cechy językowe, natknąłem się coś dziwnego:Dlaczego C# wymaga nawiasów przy użyciu nullables w wyrażeniu?

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

Czy ktoś może mi wyjaśnić dlaczego nawias są potrzebne?

+1

W pierwszym przypadku próbujesz uzyskać dostęp do elementu o nazwie 'value' w' Foo', który nie istnieje. W drugiej instrukcji "Wartość" odnosi się do właściwości 'Nullable '. – xfx

+9

Jeśli naprawdę o tym pomyślisz, wywołanie '.Value' na wyrażeniu zawierającym operator warunkowy o wartości null jest sprzeczne (możesz oczekiwać wartości zerowej lub nie).Najprawdopodobniej chciałbyś zamiast tego używać operatora koalescencyjnego, w którym to przypadku nawiasy nie są potrzebne. np .: 'Foo foo2 = foo? .Identity() ?? '; – sstan

Odpowiedz

41

Czy ktoś mógłby mi wytłumaczyć, dlaczego potrzebne są nawiasy?

ponieważ Identity() powraca ma Foo (nie Foo?), a zatem nie Value nieruchomości. Jeśli foo ma wartość NULL, wartość pusta będzie propagowana przez wywołanie Identity.

Kiedy można umieścić nawiasy wokół niego, wyniki ekspresjijest Nullable<Foo> który robi mają właściwość Value.

Należy również pamiętać, że jeśli foojest null, wtedy będzie nazywając Value na Nullable<Foo> który nie ma wartości, a dostaniesz wyjątek w czasie wykonywania. Niektóre analizatory statyczne rozpoznają, że możliwe jest wystąpienie wyjątku o wartości zerowej oczekującej na wystąpienie i ostrzeżenie.

Jeśli rozszerzyć je do swoich odpowiedników bez zerowego propagacji będzie bardziej jasne:

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() powraca Foo, dlaczego Foo foo3 = foo?.Identity(); nie skompilować?

Równowartość że byłoby:

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

Radości używania tego samego symbolu do oznaczania różnych operatorów. – BoltClock

+0

Wygrałeś z moim bardzo wolnym komputerem i siecią :) –

+0

Jeśli 'Tożsamość()' zwraca 'Foo', dlaczego' Foo foo3 = foo? .Identity(); 'nie kompiluje? – Lukas92

0

myślę, że była to dobra decyzja z zespołu C#, aby zrobić to w ten sposób. Rozważmy poniższy scenariusz:

Jeśli struct było:

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

Jeśli nie potrzebują nawias dostępu do pustych wynik, nie będzie w stanie uzyskać dostępu do właściwości ID. Ponieważ nie będzie skompilować poniżej:

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

Kiedy piszesz foo?.Identity(). co jest po . jest typu Foo zwróconej przez Identity(). Jednak w (foo?.Identity()). co jest po . jest Foo? który jest rzeczywisty wynik całego rachunku foo?.Identity().