Dlaczego nie mogę użyć operatora propagacji wartości null w wyrażeniach lambda?

103

Często używam operatora propagującego wartość null w moim kodzie, ponieważ daje mi to bardziej czytelny kod, szczególnie w długich zapytaniach nie muszę sprawdzać wartości null każdej używanej klasy.

Poniższy kod zgłasza błąd kompilacji, którego nie możemy użyć operatora propagującego wartość null w wyrażeniu lambda.

var cnt = humans.AsQueryable().Count(a => a.House?[0].Price == 5000);

Błąd :

Błąd CS8072 Lambda drzewa wyrażeń nie może zawierać operatora propagującego wartość null.

C # Można łatwo przetłumaczyć powyższy kod na kod na następujący kod, jeśli naprawdę nie można zrobić nic innego!

var cnt = humans.AsQueryable().Count(a => a.House != null && a.House[0].Price == 5000);

Jestem ciekawy, dlaczego C # nic nie robi i po prostu zgłasza błąd kompilatora?

Mohsen Sarkar
źródło
4
Foo?.Barnie jest równoważne z, Foo != null ? Foo.Bar : nullponieważ Foojest obliczane raz za pomocą operatora propagującego wartość null i dwukrotnie za pomocą warunku, więc tłumaczenie nie byłoby poprawne we wszystkich przypadkach.
Lucas Trzesniewski
3
Zauważ, że jeśli jego kod EF, istnieje możliwość, że tak naprawdę nie potrzebujemy operatora rozmnożeniowego zerową, ponieważ gdy zapytanie SQL jest konwertowany do połączenia SQL nie rzucać null :-)
Xanatos
NB: Przydałoby się również pisać var q = from c in Categories join p in Products on c equals p.Category into ps from p in ps.DefaultIfEmpty() select new { Category = c, ProductName = (p?.ProductName)??"(No products)"};zamiast pisać, ProductName = (p == null) ? "(No products)" : p.ProductNameponieważ EF obecnie nie obsługuje ?.operatora.
Matt

Odpowiedzi:

72

Jest to skomplikowane, ponieważ wyrażenia lambda drzewa (w przeciwieństwie do wyrażeń lambda delegatów) są interpretowane przez już istniejących dostawców LINQ, którzy jeszcze nie obsługują propagacji wartości null.

Konwersja do wyrażenia warunkowego nie zawsze jest dokładna, ponieważ istnieje wiele ocen, podczas ?.gdy jest tylko jedna ocena, na przykład:

customer.Where(a => c.Increment()?.Name) // Written by the user 
customer.Where(a => c.Increment() == null ? null : c.Increment().Name) // Incorrectly interpreted by an old LINQ provider

Można pójść głębiej w odpowiedniej dyskusji na CodePlex , gdzie oferowane są 3 rozwiązania: NullPropagationExpression, ConditionalExpression& hybrydą

i3arnon
źródło
26
Z pewnością nie zdziwiłbym się, gdyby niektórzy dostawcy zapytań nie mogli go obsługiwać, ale to nie jest powód, aby nie obsługiwać go w języku C #.
Servy
18
Fakt, że niektórzy dostawcy zapytań jeszcze nie obsługują to nie jest powód, aby zakazać wszystkich dostawców zapytań od zawsze jest w stanie z niego korzystać.
Servy
11
I oczywiście żaden dostawca zapytań nie zajmie czasu na obsługę takiego żądania, dopóki użytkownicy tego dostawcy nie będą w stanie faktycznie utworzyć drzew wyrażeń, które go reprezentują. Aby było to obsługiwane, pierwszą rzeczą, która musi się wydarzyć, jest to, aby lambdy mogły to reprezentować. Po tym, dostawcy zapytań mogą zacząć go obsługiwać, jeśli uznają to za stosowne. Jest też wielu dostawców, którzy zajmują się różnymi rzeczami. To nie jest tak, że EF jest jedynym dostawcą zapytań na świecie.
Servy
8
Cała sprawa o Expressionto, aby móc reprezentować wszystkie C # wyrażeń semantycznie jako kod. Nie jest to tylko mały podzbiór języka.
Servy
7
Wydaje się, że 3 lata później problem ten nadal nie został rozwiązany - czy Microsoft nie powinien być w stanie znaleźć czasu do tej pory? Wydaje się, że mają obecnie zły nawyk wykorzystywania czasu i zasobów jako wymówki dla częściowego wdrażania nowych funkcji w C # w dzisiejszych czasach.
NetMage