2010-03-09 7 views
5

Brakuje mi oczywistego: Jak uzyskać dostęp do wartości parametru w drzewie wyrażenia wyrażenia lambda?Uzyskiwanie wartości czasu działania parametru ParameterExpression w drzewie wyrażeń

Scenariusz: Dla delegata x dynamicznie tworzę wyrażenie lambda z ciałem drzewa wyrażeń, które ma ten sam podpis co delegat x. Wewnątrz ciała lamdby dokonuję sprawdzania poprawności, sprawdzania, rejestrowania danych (to jest tylko testowanie kodu, a nie produkcji), a następnie przywołuję oryginalnego delegata x z oryginalnymi parametrami. Jeśli delegat ma wartość zwracaną, również zostanie zwrócona.

To działa całkiem dobrze (w tym przekazywanie parametrów oryginalnemu uczestnikowi).

Ale uderzam w ceglaną ścianę, jeśli chcę uzyskać dostęp do oryginalnych wartości parametrów przekazanych do delegata/lambda.

pseudokod:

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"); 
+0

Kod z komentarzem wydaje się być poprawny, więc zrobiłbym to. Mówisz, że nie możesz twardo kodować indeksu. Dlaczego nie? Wydaje mi się, że to będzie dobrze. –

+0

Od tego czasu zmieniłem kod. Początkowo chciałem zapętlić wartości paramDefs w drzewie wyrażeń (Expression.Loop), odwiedzając każdy parametr jeden po drugim. Ponieważ kod działa na dowolnym typie pełnomocnika, twarde kodowanie indeksu nie zadziałałoby. Wyciągnąłem konstrukcję pętli z drzewa wyrażeń. Dla delegata z 5 parametrami, po prostu generuję pięć wyrażeń (najprostszym przypadkiem jest kopiowanie wartości każdego parametru do obiektu []). Jednak początkowe pytanie nadal mnie niepokoi - czy istnieje sposób na uzyskanie rzeczywistej wartości z instancji ParameterExpression? – dalo

Odpowiedz

3

Wygląda na to mylić kompilator wyrażenia drzew zbyt dużo (dobrze, byłem zdezorientowany tym kodem zbyt). Widzę, co próbujesz zrobić: masz element z tablicy, a następnie zdecydowałeś się przechodzić przez tablicę. Ale nie można wykonać tablicy [ParameterExpression], więc użyłeś ArrayIndex. Ale ...

Ale ArrayIndex w rzeczywistości nie zwraca "ciąg". Zwraca MethodCallExpression. Tak więc w tym wyrażeniu "Assign" faktycznie masz ParameterExpression i MethodCallExpression. Kompilator ET jest wystarczająco inteligentny, aby skompilować te wyrażenia i spróbować przypisać wyniki. Ale wynikiem twojej MethodCallExpression jest ParameterExpression. Kiedy miałeś paramDefs [0], miałeś ParameterExpression od razu i kompilator mógł obsłużyć to. Ale kompilowanie zagnieżdżonych wyrażeń jest trudniejsze i nie jest jasne, czy naprawdę chcesz skompilować to zagnieżdżone wyrażenie, czy też nie.

Co możesz zrobić, to samodzielnie skompilować i wywołać MethodCallExpression, więc będziesz miał parametr ParameterExpression w wyrażeniu Assign (jak przedtem). może to wyglądać tak:

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

Ale to może być bardzo ciężka pod względem wydajności (plus kod jest brzydki). Więc trzymałbym się twojego pomysłu wyciągnięcia pętli z drzewa ekspresji.

Powiązane problemy