Por que não posso usar o operador de propagação nula em expressões lambda?

102

Costumo usar o operador de propagação nulo em meu código porque ele me dá um código mais legível, especialmente em consultas longas, não preciso verificar o valor nulo de todas as classes usadas.

O código a seguir lança um erro de compilação de que não podemos usar o operador de propagação nulo em lambda.

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

O erro :

Erro CS8072 Um lambda de árvore de expressão não pode conter um operador de propagação nulo.

C # poderia facilmente traduzir o código acima para o código a seguir se realmente não puder fazer mais nada!

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

Estou curioso para saber por que o C # não faz nada e simplesmente lança um erro do compilador?

Mohsen Sarkar
fonte
4
Foo?.Barnão é equivalente a Foo != null ? Foo.Bar : nullporque Fooé avaliado uma vez com o operador de propagação nula e duas vezes com o condicional, de modo que a tradução não seria correta em todos os casos.
Lucas Trzesniewski,
3
Observe que se for código para EF, existe a possibilidade de você realmente não precisar do operador de propagação nulo, porque quando uma consulta é convertida em chamada SQL, o SQL não lança nulos :-)
xanatos
NB: Também seria útil escrever em 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)"};vez de ter que escrever ProductName = (p == null) ? "(No products)" : p.ProductNameporque EF atualmente não suporta o ?.operador.
Matt

Respostas:

72

É complicado, pois os lambdas da árvore de expressão (ao contrário dos lambdas do delegado) são interpretados por provedores LINQ já existentes que ainda não oferecem suporte à propagação nula.

A conversão para uma expressão condicional nem sempre é precisa, pois há várias avaliações, enquanto com ?.há apenas uma única avaliação, por exemplo:

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

Você pode ir mais fundo na relevante discussão no CodePlex , onde são oferecidos 3 soluções: NullPropagationExpression, ConditionalExpressione um híbrido

i3arnon
fonte
23
Eu certamente não ficaria surpreso se alguns provedores de consulta não pudessem oferecer suporte, mas isso não é um motivo para não ter a linguagem C # suportando.
Servy
16
O fato de que certos provedores de consulta ainda não apoiá-lo não é uma razão para proibir todos os provedores de consulta de nunca ser capaz de usá-lo.
Servy
10
E, obviamente, nenhum provedor de consulta vai perder tempo para dar suporte ao tratamento de tal solicitação até que os usuários desse provedor possam realmente criar árvores de expressão que o representem. Para que isso seja suportado, a primeira coisa que precisa acontecer é que os lambdas possam representá-lo. Depois que isso existir, os provedores de consulta podem começar a oferecer suporte, conforme acharem apropriado. Também existem muitos provedores por aí fazendo todos os tipos de coisas diferentes. Não é como se a EF fosse o único provedor de consultas do mundo.
Servy
7
Toda a ponto de Expressionse ser capaz de representar todas as expressões C # semanticamente como código. Não foi projetado para ser apenas um pequeno subconjunto da linguagem.
Servy
6
Parece que isso ainda não foi resolvido 3 anos depois - a Microsoft não deveria ter conseguido encontrar o tempo agora? Eles parecem ter o mau hábito de usar tempo e recursos como desculpa para implementar pela metade novos recursos em C # atualmente.
NetMage de