开发者

Local variable and expression trees

开发者 https://www.devze.com 2023-03-31 02:01 出处:网络
I am learning expression trees in C#. I am stuck now for a while: string filterString = \"ruby\"; Expression<Func<string, bool>> expression = x => x == filterString;

I am learning expression trees in C#.

I am stuck now for a while:

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

How can I construct this expression by code? There is no sample how to capture a local variable. This one is easy:

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

This would be:

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

The debugger prints the following for (x => x == filterString) :

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

Thanks for shedding some light on this topic.开发者_如何转开发


Capturing a local variable is actually performed by "hoisting" the local variable into an instance variable of a compiler-generated class. The C# compiler creates a new instance of the extra class at the appropriate time, and changes any access to the local variable into an access of the instance variable in the relevant instance.

So the expression tree then needs to be a field access within the instance - and the instance itself is provided via a ConstantExpression.

The simplest approach for working how to create expression trees is usually to create something similar in a lambda expression, then look at the generated code in Reflector, turning the optimization level down so that Reflector doesn't convert it back to lambda expressions.


This code wraps the expression in a closure Block that treats the local variable as a constant.

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


An old question but I came to it when trying to do something similar building expressions for Linq-to-entities (L2E) In that case you cannot use Expression.Block as it cannot be parsed down to SQL.

Here is an explicit example following Jon's answer which would work with L2E. Create a helper class to contain the value of the filter:

class ExpressionScopedVariables
{
    public String Value;
}

Build the tree thus:

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

And then replace the constant in the original code with the member access expression:

BinaryExpression equals = Expression.Equal(stringParam, access);
Expression<Func<string, bool>> lambda1 =
    Expression.Lambda<Func<string, bool>>(
        equals,
        new ParameterExpression[] { stringParam });
0

精彩评论

暂无评论...
验证码 换一张
取 消