yonexbat
yonexbat

Reputation: 3012

Local variable and expression trees

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.

Upvotes: 34

Views: 19340

Answers (3)

James Close
James Close

Reputation: 932

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

Upvotes: 11

Nescio
Nescio

Reputation: 28423

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

Upvotes: 10

Jon Skeet
Jon Skeet

Reputation: 1500735

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.

Upvotes: 43

Related Questions