Warum kann ich nicht manuell den gleichen Ausdrucksbaum erstellen, den mein gerader Lambda erzeugt?

c# expression-trees lambda

Frage

Ich bin durchgegangen und habe meinen Kopf eine Weile gegen die Wand geschlagen, jetzt nach verschiedenen Phrasen und Schlüsselwörtern gesucht, aber ich kann nichts finden, was einer Antwort nahe kommt, also hoffe ich, dass hier jemand etwas Licht erhaschen kann.

Im Grunde arbeite ich daran, tief in die Manipulation, Erstellung und Modifikation von Expression Trees in C # 4.0 einzutauchen

Ich bin auf eine seltsame Anomalie gestoßen, die ich nicht verstehen kann

wenn ich sowas schreibe

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

Wenn ich debugge und den Ausdrucksbaum sehe, sieht es ähnlich aus

  • F (Nodetyp = Lambda)
    • Body (NodeType = Hinzufügen)
      • Links (NodeType = Hinzufügen)
        • Links (NodeType = Konstante, Wert = "This")
        • Richtig (NodeType = Bedingt)
          • IfFalse (NodeType = Hinzufügen)
            • Links (NodeType = Parameter, Name = "Einfügen")
            • Richtig (NodeType = Konstante, Wert = "")
          • IfTrue (NodeType = Konstante, Wert = "")
          • Test (NodeType = Gleich)
            • Links (NodeType = Parameter, Name = "Einfügen")
            • Richtig (NodeType = Konstante, Wert = "")
      • Richtig (NodeType = Konstante, Wert = "Das")
    • Parameterter (Anzahl = 1)
      • Parameter [0] (NodeType = Parameter, Name = "Einfügen")

Ich kann anrufen

Console.WriteLine(InsertAString.Compile()("Is Something In-between"));

Und ich gehe raus, wie ich es erwarte

"Das ist etwas dazwischen"

Wenn ich nun versuche, das manuell mit den statischen Methoden der Expression-Basisklasse neu aufzubauen, stoße ich auf ein interessantes Problem. (Ich habe jeden Schritt in einen eigenen Ausdruck für Debugging-Zwecke unterteilt)

ParameterExpression Insert = Expression.Parameter(typeof(object), "Insert");
ConstantExpression This = Expression.Constant("This ");
ConstantExpression That = Expression.Constant("That");
ConstantExpression Space = Expression.Constant(" ");
ConstantExpression NoCharacter = Expression.Constant("");
BinaryExpression InsertPlusSpace = Expression.Add(Insert,Space);
BinaryExpression InsertEqualsNoCharacter = Expression.Equal(Insert,NoCharacter);
ConditionalExpression InsertPlusSpaceOrNothing = Expression.IfThenElse(InsertEqualsNoCharacter,NoCharacter,InsertPlusSpace);
BinaryExpression ThisPlusInsertPlusSpaceOrNothing = Expression.Add(This,InsertPlusSpaceOrNothing);
BinaryExpression ThisPlusInsertPlusSpaceOrNothingPlusThat = Expression.Add(ThisPlusInsertPlusSpaceOrNothing, That);
Lambda Lambda = Expression.Lambda(ThisPlusInsertPlusSpaceOrNothingPlusThat, Middle);
Expression<Func<string,string>> InsertAString = Lambda as Expression<Func<string,string>>   

Basierend auf den Werten der generierten Ausdrucksbaumstruktur erstellen Sie denselben Basisbaum wie oben (mindestens mit dem gleichen "Look").

Alles läuft gut, bis Sie zu dieser Linie kommen

BinaryExpression InsertPlusSpace = Expression.Add(Insert,Space);

Der Compiler wirft eine InvalidOperationException wurde nicht behandelt

Der binäre Operator Add ist nicht für 'System.String' und 'System.String' definiert

Warum ist das nun?

Warum, wenn ich C # ein Lambda in einen Ausdruck umwandeln lasse, benutzt es offensichtlich den Add NodeType, und das Typen-Display zeigt an, dass es System.String definitiv benutzt, wenn ich es manuell mache und den Code nicht weiter laufen lasse?

Als letzten Hinweis habe ich sogar folgendes versucht:

BinaryExpression InsertPlusSpace = Expression.MakeBinary( ExpressionType.Add,Insert,Space);

Derselbe Fehler.

Ich bin neugierig, warum es mindestens mit dem scheint, was ich bis jetzt gefunden habe, dass String-Verkettung in Ausdrucksbäumen nur funktioniert, wenn nicht manuell versucht wird, einen Ausdrucksbaum zu erstellen, der Konstanten und Variablen vom Typ System.String hinzufügt.

Vielen Dank im Voraus für die Antworten.

Beliebte Antwort

Überprüfen Sie die Dokumentation: Der Operator '+' ist in der String Klasse nicht definiert. Ich denke, der Compiler weiß nur, dass er "die Strings verketten" muss, und er wandelt es in einen Aufruf von Concat . Wenn Sie also Expression.Add aufrufen, müssen Sie die Methode angeben, die die Operation implementiert (in diesem Fall die String.Concat Methode).

Ich habe den Ausdruck mit Reflector dekompiliert, es ergibt folgendes Ergebnis (umformatiert):

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 });

(Beachten Sie, dass methodof kein tatsächlicher Operator ist, sondern nur, was Reflector für den ldtoken IL ldtoken . In C # müssen Sie die Methode mithilfe der Reflektion abrufen.)



Lizenziert unter: CC-BY-SA with attribution
Nicht verbunden mit Stack Overflow
Ist diese KB legal? Ja, lerne warum
Lizenziert unter: CC-BY-SA with attribution
Nicht verbunden mit Stack Overflow
Ist diese KB legal? Ja, lerne warum