Pourquoi ne puis-je pas créer manuellement le même arbre d'expression que celui que produit mon lambda direct?

c# expression-trees lambda

Question

Je suis passé au travers et je me suis cogné la tête contre le mur pendant un moment, j'ai recherché différentes expressions et mots-clés, mais je ne trouve rien qui ressemble à une réponse, alors j'espère que quelqu'un ici pourra nous éclairer un peu.

En gros, je travaille à plonger assez profondément dans la manipulation, la création et la modification des arbres d'expression en C # 4.0.

Je suis tombé sur une étrange anomalie que je n'arrive pas à comprendre.

si j'écris quelque chose comme ça

Expression<Func<string,string>> InsertAString = (Insert) => "This " + (Insert == "" ? "" : Insert + " ") + "That";

Quand je reçois un débogage et que je regarde l’arbre des expressions, cela ressemble à ceci

  • F (NodeType = Lambda)
    • Corps (NodeType = Ajouter)
      • Left (NodeType = Add)
        • Gauche (NodeType = Constant, Value = "This")
        • Right (NodeType = Conditionnel)
          • IfFalse (NodeType = Add)
            • Gauche (NodeType = Parameter, Name = "Insert")
            • Right (NodeType = Constant, Value = "")
          • IfTrue (NodeType = Constant, Value = "")
          • Test (NodeType = Equal)
            • Gauche (NodeType = Parameter, Name = "Insert")
            • Right (NodeType = Constant, Value = "")
      • Right (NodeType = Constant, Value = "That")
    • Paramètres (Count = 1)
      • Paramètres [0] (NodeType = Parameter, Name = "Insert")

je peux appeler

Expression<Func<string,string>> InsertAString = (Insert) => "This " + (Insert == "" ? "" : Insert + " ") + "That";

Et je sors comme prévu

"C'est quelque chose entre les deux"

Maintenant, si j’essaie de reconstruire manuellement à l’aide des méthodes statiques de la classe de base Expression, je rencontre un problème intéressant. (J'ai décomposé chaque étape en sa propre expression à des fins de débogage)

Expression<Func<string,string>> InsertAString = (Insert) => "This " + (Insert == "" ? "" : Insert + " ") + "That";

Celui basé sur les valeurs de l'arbre d'expression généré ci-dessus recrée le même arbre d'expression de base que ci-dessus (au moins avec le même "Look")

Tout va bien jusqu'à ce que vous arriviez à cette ligne

Expression<Func<string,string>> InsertAString = (Insert) => "This " + (Insert == "" ? "" : Insert + " ") + "That";

Le compilateur lève une exception InvalidOperationException non gérée

L'opérateur binaire Add n'est pas défini pour 'System.String' et 'System.String'.

Maintenant pourquoi est-ce?

Pourquoi, quand je laisse C # convertir un Lambda en Expression, utilise-t-il évidemment le Add NodeType, et l'affichage Types montre-t-il qu'il utilise définitivement System.String?

En guise de note finale, j'ai même essayé ce qui suit:

Expression<Func<string,string>> InsertAString = (Insert) => "This " + (Insert == "" ? "" : Insert + " ") + "That";

Même erreur.

Je suis curieux de savoir pourquoi, du moins avec ce que j'ai pu constater jusqu'à présent, la concaténation de chaînes dans les arbres d'expression ne fonctionne que si vous n'essayez pas de créer manuellement un arbre d'expression qui ajoute des constantes et des variables de type System.String.

Merci à tous pour les réponses.

Réponse populaire

Consultez la documentation: l'opérateur '+' n'est en réalité pas défini dans la classe String . Je suppose que le compilateur sait juste que cela signifie "concaténer les chaînes", et le transforme en un appel à Concat . Ainsi, lorsque vous appelez Expression.Add , vous devez spécifier la méthode qui implémente l'opération (dans ce cas, la méthode String.Concat ).

J'ai décompilé l'expression avec Reflector, cela donne le résultat suivant (reformaté):

ParameterExpression expression2;
Expression<Func<string, string>> expression =
    Expression.Lambda<Func<string, string>>(
        Expression.Add(
            Expression.Add(
                Expression.Constant("This ", typeof(string)),
                Expression.Condition(
                    Expression.Equal(
                        expression2 = Expression.Parameter(typeof(string), "Insert"),
                        Expression.Constant("", typeof(string)),
                        false,
                        (MethodInfo) methodof(string.op_Equality)),
                    Expression.Constant("", typeof(string)),
                    Expression.Add(
                        expression2,
                        Expression.Constant(" ", typeof(string)),
                        (MethodInfo) methodof(string.Concat))),
                (MethodInfo) methodof(string.Concat)),
            Expression.Constant("That", typeof(string)),
            (MethodInfo) methodof(string.Concat)),
        new ParameterExpression[] { expression2 });

(Notez que methodof n’est pas un opérateur réel, c’est ce que Reflector montre pour l’instruction ldtoken . En C #, vous devez récupérer la méthode en utilisant la réflexion.)




Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Est-ce KB légal? Oui, apprenez pourquoi
Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Est-ce KB légal? Oui, apprenez pourquoi