Abrufen des Laufzeitwerts einer ParameterExpression in einer Ausdrucksbaumstruktur

c# c#-4.0 delegates expression-trees lambda

Frage

Ich vermisse das Offensichtliche: Wie greife ich auf den Wert eines Parameters in einem Lambda-Ausdrucksbaum zu?

Szenario: Für einen Delegaten x erstelle ich dynamisch einen Lambda-Ausdruck mit einem Ausdrucksbaumtext, der dieselbe Signatur wie der Delegat x hat. Innerhalb des Körpers der Lamdba mache ich eine Validierung, überprüfe, protokolliere Zeug (das ist nur Code testen, nicht Produktion), und dann rufe ich den ursprünglichen Delegierten x mit den ursprünglichen Parametern auf. Wenn der Delegat einen Rückgabewert hat, wird dieser ebenfalls zurückgegeben.

Das funktioniert ganz gut (einschließlich der Übergabe der Parameter an den ursprünglichen Delegierten).

Aber ich stoße auf eine Mauer, wenn ich auf die ursprünglichen Parameterwerte zugreifen möchte, die an den Delegaten / Lambda übergeben werden.

Pseudocode:

var del = new Func<string, int>(_=> {return 42;});
var paramDefs = Array.ConvertAll<ParameterInfo, ParameterExpression>(del.Method.GetParameters(), _ => { return Expression.Parameter(_.ParameterType, _.Name); });
var variableTest = Expression.Variable(typeof(string), "str");

var expression = Expression.Block(
  new [] { variableTest },
  // this line assigns the actual run time value (which is what I need) of the parameter to the variable - but I cannot hardcode the index.
  //Expression.Assign(variableTest, paramDefs[0]) 
  // this line would assigns the ParameterExpression object (causing a run-time exception since the type of the variable is string) ... I need the _value_ of the first (or nth) parameter.
  Expression.Assign(variableTest, Expression.ArrayIndex(Expression.Constant(paramDefs), Expression.Constant(0)))
);
var lamdba = Expression.Lambda(del.GetType(), expression, "foo", paramDefs);
var del2 = lamdba.Compile() as Func<string, int>;
del2("this is a test");

Akzeptierte Antwort

Sieht so aus, als hättest du den Ausdrucksbaum-Compiler zu sehr verwirrt (nun, ich war auch von diesem Code verwirrt). Ich kann sehen, was Sie versucht haben zu tun: Sie haben ein Element aus einem Array, dann haben Sie sich entschieden, das Array zu durchlaufen. Aber Sie konnten Array [ParameterAusdruck] nicht ausführen, also haben Sie ArrayIndex verwendet. Aber...

Aber ArrayIndex gibt tatsächlich keine "Zeichenfolge" zurück. Es gibt MethodCallExpression zurück. In diesem "Assign" -Ausdruck haben Sie also ParameterExpression und MethodCallExpression. Der ET-Compiler ist intelligent genug, um diese Ausdrücke zu kompilieren und zu versuchen, Ergebnisse zuzuordnen. Aber das Ergebnis Ihrer MethodCallExpression ist ParameterExpression. Wenn Sie paramDefs [0] hatten, hatten Sie sofort ParameterExpression und der Compiler konnte damit umgehen. Das Kompilieren verschachtelter Ausdrücke ist jedoch schwieriger und es ist völlig unklar, ob Sie diesen geschachtelten Ausdruck wirklich kompilieren wollen oder nicht.

Was Sie tun können, ist das Compile und Aufruf der MethodCallExpression selbst, so dass Sie ParameterExpression im Assign-Ausdruck haben (wie Sie vorher hatten). Es könnte so aussehen:

// Replace Assign in your Block expression.
Expression.Assign(variableTest, Expression.Lambda<Func<ParameterExpression>>(Expression.ArrayIndex(Expression.Constant(paramDefs), Expression.Constant(0))).Compile()()),

Aber es könnte in Bezug auf die Leistung sehr schwer sein (plus der Code ist hässlich). Ich würde also bei Ihrer Idee bleiben, die Schleife aus dem Ausdrucksbaum zu ziehen.




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