Pourquoi ne puis-je pas utiliser l'opérateur de propagation null dans les expressions lambda?

103

J'utilise souvent un opérateur de propagation nul dans mon code car il me donne un code plus lisible, en particulier dans les requêtes longues, je n'ai pas à vérifier la valeur nulle de chaque classe utilisée.

Le code suivant lève une erreur de compilation selon laquelle nous ne pouvons pas utiliser l'opérateur de propagation nul dans lambda.

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

L'erreur :

Erreur CS8072 Une arborescence d'expression lambda ne peut pas contenir d'opérateur de propagation nul.

C # pourrait facilement traduire le code ci-dessus en code en code suivant s'il ne peut vraiment rien faire d'autre!

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

Je suis curieux de savoir pourquoi C # ne fait rien et lève simplement une erreur de compilation?

Mohsen Sarkar
la source
4
Foo?.Barn'est pas équivalent à Foo != null ? Foo.Bar : nullcar Fooest évalué une fois avec l'opérateur de propagation nul et deux fois avec le conditionnel, de sorte que la traduction ne serait pas correcte dans tous les cas.
Lucas Trzesniewski
3
Notez que si son code pour EF, il est possible que vous n'ayez pas vraiment besoin de l'opérateur de propagation nul, car lorsqu'une requête est convertie en appel SQL, SQL ne lance pas de
valeurs
NB: Il serait également utile d'écrire 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)"};au lieu d'avoir à écrire ProductName = (p == null) ? "(No products)" : p.ProductNamecar EF ne prend actuellement pas en charge l' ?.opérateur.
Matt du

Réponses:

72

C'est compliqué car les lambdas d'arbre d'expression (contrairement aux lambdas de délégué) sont interprétés par des fournisseurs LINQ déjà existants qui ne prennent pas encore en charge la propagation null.

La conversion en une expression conditionnelle n'est pas toujours précise car il y a plusieurs évaluations alors qu'avec ?.une seule évaluation, par exemple:

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

Vous pouvez aller plus loin dans la pertinente discussion sur CodePlex où 3 solutions sont proposées: NullPropagationExpression, ConditionalExpressionet un hybride

i3arnon
la source
26
Je ne serais certainement pas surpris si certains fournisseurs de requêtes ne pouvaient pas le prendre en charge, mais ce n'est pas une raison pour que le langage C # ne le prenne pas en charge.
Servy
18
Le fait que certains fournisseurs de requête ne sont pas encore en charge ce n'est pas une raison d'interdire tous les fournisseurs de requête de jamais pouvoir l' utiliser.
Servy
11
Et évidemment, aucun fournisseur de requêtes ne prendra le temps de prendre en charge le traitement d'une telle demande jusqu'à ce que les utilisateurs de ce fournisseur soient en mesure de créer des arborescences d'expressions qui la représentent. Pour que cela soit pris en charge, la première chose à faire est que les lambdas soient capables de le représenter. Une fois que cela existe, les fournisseurs de requêtes peuvent commencer à le prendre en charge, s'ils le jugent approprié. Il existe également de nombreux fournisseurs qui font toutes sortes de choses. Ce n'est pas comme si EF était le seul fournisseur de requêtes au monde.
Servy
8
L' intérêt de Expressionest de pouvoir représenter toutes les expressions C # sémantiquement sous forme de code. Il n'est pas conçu pour être juste un petit sous-ensemble du langage.
Servy
7
Il semble que cela ne soit toujours pas résolu 3 ans plus tard - Microsoft n'aurait-il pas dû être en mesure de trouver l'heure maintenant? Ils semblent avoir une mauvaise habitude d'utiliser le temps et les ressources comme excuse pour implémenter à moitié de nouvelles fonctionnalités en C # ces jours-ci.
NetMage