Локальные переменные и деревья выражений - PullRequest
23 голосов
/ 28 августа 2011

Я изучаю деревья выражений в C #.

Теперь я застрял на некоторое время:

string filterString = "ruby";
Expression<Func<string, bool>> expression = x => x == filterString;

Как я могу построить это выражение по коду?Там нет образца, как захватить локальную переменную.Это просто:

Expression<Func<string, bool>> expression = x => x == "ruby";

Это будет:

ParameterExpression stringParam = Expression.Parameter(typeof(string), "x");
Expression constant = Expression.Constant("ruby");
BinaryExpression equals = Expression.Equal(stringParam, constant);
Expression<Func<string, bool>> lambda1 =
    Expression.Lambda<Func<string, bool>>(
        equals,
        new ParameterExpression[] { stringParam });

Отладчик печатает следующее для (x => x == filterString):

{x => (x == value (Predicate.Program + <> c__DisplayClass3) .filterString)}

Спасибо, что пролили свет на эту тему.

Ответы [ 3 ]

30 голосов
/ 28 августа 2011

Захват локальной переменной фактически выполняется путем "подъема" локальной переменной в переменную instance сгенерированного компилятором класса. Компилятор C # создает новый экземпляр дополнительного класса в соответствующее время и изменяет любой доступ к локальной переменной на доступ к переменной экземпляра в соответствующем экземпляре.

Таким образом, дерево выражений должно быть полем доступа внутри экземпляра, а сам экземпляр предоставляется через ConstantExpression.

Простейший подход к работе над созданием деревьев выражений обычно заключается в создании чего-то похожего в лямбда-выражении, а затем в сгенерированном коде с помощью Reflector понижается уровень оптимизации, чтобы Reflector не преобразовывал его обратно в лямбда-выражения .

7 голосов
/ 28 августа 2011

Этот код переносит выражение в закрывающий блок, который обрабатывает локальную переменную как константу.

 string filterString = "ruby";

 var filterStringParam = Expression.Parameter(typeof(string), "filterString");
 var stringParam = Expression.Parameter(typeof(string), "x");

 var block = Expression.Block(
 // Add a local variable.
 new[] { filterStringParam },
 // Assign a constant to the local variable: filterStringParam = filterString
 Expression.Assign(filterStringParam, Expression.Constant(filterString, typeof(string))),
 // Compare the parameter to the local variable
 Expression.Equal(stringParam, filterStringParam));

 var x = Expression.Lambda<Func<string, bool>>(block, stringParam).Compile();
5 голосов
/ 05 октября 2017

Старый вопрос, но я пришел к нему, когда пытался сделать что-то похожее для построения выражений для Linq-to-entity (L2E). В этом случае вы не можете использовать Expression.Block, так как он не может быть проанализирован до SQL.

Вот явный пример после ответа Джона, который будет работать с L2E. Создайте вспомогательный класс, который будет содержать значение фильтра:

class ExpressionScopedVariables
{
    public String Value;
}

Постройте дерево так:

var scope = new ExpressionScopedVariables { Value = filterString};
var filterStringExp = Expression.Constant(scope);
var getVariable = typeof(ExpressionScopedVariables).GetMember("Value")[0];
var access = Expression.MakeMemberAccess(filterStringExp, getVariable);

А затем замените константу в исходном коде выражением доступа к члену:

BinaryExpression equals = Expression.Equal(stringParam, access);
Expression<Func<string, bool>> lambda1 =
    Expression.Lambda<Func<string, bool>>(
        equals,
        new ParameterExpression[] { stringParam });
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...