Local Variable and Expression Trees

Local variable and expression trees

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.

declaring local variables in c# expression trees bound to a block

Nevermind - this was a nasty bug where the assignment $num was referencing a different object but same name parameter expression which was different to the parameter expression in the block. Parameter Expressions with the same name are not necessarily the same parameter expressions!

Using a local variable in an expression tree

As suspected, it seems that a ConstantExpression is not the way to obtain the value from a local variable.

What I needed to do was create a private class to store the variable in, and then I was able to access it with a field MemberExpression

private class ValueHolder<T>
{
public IQueryable<T> History;
}

Then in my method I was able to have the expression evaluated using this:

var valueHolder = new ValueHolder<T>
{
History = data
};

var c = Expression.Parameter(typeof(T), "c");
var constantEx = Expression.Constant(valueHolder);
var fieldEx = Expression.Field(constantEx, valueHolder.GetType().GetField("History"));

Expression Tree how do I capture a local variable

In your case, you don't actually need to capture any local variable, you can just use Expression.Constant(planetName).

If you then call it with, for example, MakeDynamicPredicate("Pluto"), the generated expression will be as if you wrote p => p.name == "Pluto".

Passing local variables to expression tree as collection list

When you declare the block, you need to pass any variables referenced with in the block as the first argument,

BlockExpression block = Expression.Block(
new ParameterExpression[] { i },
lines.ToArray()
);

How can i access the value of a local variable from within an expression tree

The local variable which has been captured by the lambda and included in the expression tree, will at that time really be a field on some compiler-generated class. This works on my version of .NET:

void Test<T>(Expression<Func<T>> func)
{
var body = func.Body;

if (body.NodeType == ExpressionType.Constant)
{
Console.WriteLine(((ConstantExpression)body).Value);
}
else
{
var memberExpression = (MemberExpression)body;

var @object =
((ConstantExpression)(memberExpression.Expression)).Value; //HERE!

if (memberExpression.Member.MemberType == MemberTypes.Field)
{
Console.WriteLine(((FieldInfo)memberExpression.Member).GetValue(@object));
}
else if (memberExpression.Member.MemberType == MemberTypes.Property)
{
Console.WriteLine(((PropertyInfo)memberExpression.Member).GetValue(@object));
}
}
}

Of course, you can also "cheat":

void Test<T>(Expression<Func<T>> func)
{
Console.WriteLine(func.Compile()());
}

How to get expression for a local variable

When you are stuck with expressions, you can consider SharpLab. If I understood you correctly, you want to have an expression that looks like this:

Expression<Func<AIGameBodyBehaviourFactoryArguments, AIGameBodyBehaviour>> expression =
(arguments) => new AIGameBodyBehaviour(arguments);

SharpLab will show you that the compiler will turn this line into

ParameterExpression parameterExpression = Expression.Parameter(typeof(AIGameBodyBehaviourFactoryArguments), "arguments");
ConstructorInfo constructor = (ConstructorInfo)MethodBase.GetMethodFromHandle((RuntimeMethodHandle)/*OpCode not supported: LdMemberToken*/);
Expression[] array = new Expression[1];
array[0] = parameterExpression;
NewExpression body = Expression.New(constructor, (IEnumerable<Expression>)array);
ParameterExpression[] array2 = new ParameterExpression[1];
array2[0] = parameterExpression;
Expression<Func<AIGameBodyBehaviourFactoryArguments, AIGameBodyBehaviour>> expression = Expression.Lambda<Func<AIGameBodyBehaviourFactoryArguments, AIGameBodyBehaviour>>(body, array2);

(See Sharplab)

You can see that the same instance for ParameterExpression is used as the second parameter in Expression.New and in the second parameter in Expression.Lambda. Hence your code should look like this:

foreach (Type t in assembly.GetTypes()) {
if (t.IsAssignableTo(typeof(AIGameBodyBehaviour))) {
var parameterExpression = Expression.Parameter(typeof(AIGameBodyBehaviourFactoryArguments), "arguments");
AIGameBodyBehaviourFactory factory = new AIGameBodyBehaviourFactory() {
Id = t.Name,
NewAIGameBodyBehaviour = Expression.Lambda<Func<AIGameBodyBehaviourFactoryArguments, AIGameBodyBehaviour>>(

Expression.New(
t.GetConstructor(new Type[] {typeof(AIGameBodyBehaviourFactoryArguments) }),
new Expression[] {
parameterExpression
}
),
new ParameterExpression[] {
parameterExpression
}
).Compile()
};
EntityLoaderInterface.AddAIGameBodyBehaviourFactory(factory);
}
}


Related Topics



Leave a reply



Submit