2010-12-09 58 views

Répondre

3

Oui, cela devrait être possible. Cependant, il est un peu plus compliqué qu'il n'y paraît sur la surface pour implémenter correctement un réécrivain d'arbre d'expression. Surtout si vous voulez gérer correctement les champs, les propriétés, les propriétés indexées, les appels de méthodes et d'autres constructions valides dans des expressions arbitraires.

Il peut également ne pas être l'opération la plus performante puisque pour évaluer l'expression vous devez compiler dynamiquement l'arbre d'expression dans une fonction lambda à chaque fois.

Il existe un implementation on this pattern on CodePlex. Je ne l'ai jamais utilisé personnellement, donc je ne peux pas dire si c'est bien implémenté, ou s'il gère tous les cas que j'ai décrits.

Une alternative à la création d'un arbre d'expression re-écrivain, est d'écrire Maybe() d'accepter une fonction lambda (plutôt que d'un arbre d'expression) et attraper tout ArgumentNullException jeté, le retour default(T) dans ces cas. Il frotte à beaucoup de gens la mauvaise façon d'utiliser des exceptions pour le contrôle de flux de cette façon ... mais c'est certainement une implémentation plus facile à faire. Personnellement, je l'évite moi-même car il peut masquer des erreurs de référence nulles dans les méthodes appelées dans le cadre de l'expression, ce qui n'est pas souhaitable.

0

réponse si les Simpler objets ne coûtent pas cher pour créer et que vous voulez éviter les contrôles nuls:

myOrder.NewIfNull().Customer.NewIfNull().City; 

Ceci renvoie null ou une valeur initiale définie dans le constructeur ou d'un champ initialiseur pour la ville. NewIfNull n'est pas intégré, mais il est vraiment facile:

public static T NewIfNull<T>(this T input) where T:new() 
{ 
    return input ?? new T(); 
} 
+0

La création de nouveaux objets peut avoir des conséquences imprévues. – Amy

0

Je sais que ma mise en œuvre de peut-être (selon l'article CodeProject) a un coût, mais je suis sûr que c'est rien par rapport à l'idée d'obtenir un Expression<T> impliqué là. Fondamentalement, vous parlez de réflexion tout le chemin. Cela ne me dérangerait pas si c'était pré-compilé, style Roslyn, mais nous n'y sommes pas encore.

Je dirais que l'avantage de ma mise en œuvre va bien au-delà du mythique. opérateur. La possibilité d'écrire un algorithme entier en utilisant une chaîne comme celle-ci signifie que vous pouvez injecter vos propres créations (telles que If, Do, etc.) et fournir votre propre logique spécialisée. Je me rends compte que c'est plus compliqué que ce que vous essayez de faire ici, mais il ne semble pas que nous allons obtenir un opérateur point coalescent nul dans C# 5.

1

Quelques points qui me viennent à l'esprit:

  • .Solutions fonctionnent très bien pour les objets de mémoire, mais des ennuis avec EF, car ces appels statiques ne peuvent pas être convertis pour fonctionner contre le stockage persistant (qui est, SQL DB). Cela limite considérablement le champ d'application.

  • Je voudrais pratiquement toujours savoir si la chaîne a produit un résultat valable. Par conséquent, je vais avoir un bloc conditionnel if(city == null) dans tous les cas.

  • Toute solution actuelle autre que "laide" implique des expressions.

Par conséquent, mon choix serait quelque chose comme

var property = (() => myOrder.Customer.City); 
city = HasValue(property) ? property.Invoke() : "unknown"; 

HasValue(Expression e) promenades à travers l'arbre d'expression LINQ récursive jusqu'à ce qu'il soit atteint la fin (retour true) ou rencontre propriété null-valeur (soit faux). La mise en œuvre doit être simple, utilisez MethodInfo Member de classe MemberExpression pour analyser l'AST. On pourrait aussi implémenter getter de cette manière comme Brian l'a suggéré, mais j'aime mieux ci-dessus car HasValue renvoie toujours bool. En outre:

  • Les invocations de membres peuvent également être gérées.
  • Évaluation pourrait être faite comme myOrder.HasValue(x => x.Customer.City) mais cela apporte quelques complications.