Updated Question Further Down
I've been experimenting with expression trees in .NET 4 to generate code at runtime and I've been trying to implement the foreach
statement by building an expression tree.
In the end, the expression should be able to generate a delegate that does this:
Action<IEnumerable<int>> action = source =>
{
var enumerator = source.GetEnumerator();
while(enumerator.MoveNext())
{
var i = enumerator.Current;
// the body of the foreach that I don't currently have yet
}
}
I've come up with the following helper method that generates a BlockExpression from an IEnumerable:
public static BlockExpression ForEachExpr<T>(this IEnumerable<T> source, string collectionName, string itemName)
{
var item = Expression.Variable(typeof(T), itemName);
var enumerator = Expression.Variable(typeof(IEnumerator<T>), "enumerator");
var param = Expression.Parameter(typeof(IEnumerable<T>), collectionName);
var doMoveNext = Expression.Call(enumerator, typeof(IEnumerator).GetMethod("MoveNext"));
var assignToEnum = Expression.Assign(enumerator, Expression.Call(param, typeof(IEnumerable<T>).GetMethod("GetEnumerator")));
var assignCurrent = Expression.Assign(item, Expression.Property(enumerator, "Current"));
var @break = Expression.Label();
var @foreach = Expression.Block(
assignToEnum,
Expression.Loop(
Expression.IfThenElse(
Expression.NotEqual(doMoveNext, Expression.Constant(false)),
assignCurrent
, Expression.Break(@break))
,@break)
);
return @foreach;
}
The following code:
var ints = new List<int> { 1, 2, 3, 4 };
var expr = ints.ForEachExpr("ints", "i");
var lambda = Expression.Lambda<Action<IEnumerable<int>>>(expr, Expression.Parameter(typeof(IEnumerable<int>), "ints"));
Generates this expression tree:
.Lambda #Lambda1<System.Action`1[System.Collections.Generic.IEnumerable`1[System.Int32]]>(System.Collections.Generic.IEnumerable`1[System.Int32] $ints)
{
.Block() {
$enumerator = .Call $ints.GetEnumerator();
.Loop {
.If (.Call $enumerator.MoveNext() != False) {
$i = $enumerator.Current
} .Else {
.Break #Label1 { }
}
}
.LabelTarget #Label1:
}
}
This seems to be OK, but calling Compile
on that expression results in an exception:
"variable 'enumerator' of type 'System.Collections.Generic.IEnumerator`1[System.Int32]' referenced from scope '', but it is not defined"
Didn't I define it here:
var enumerator = Expression.Variable(typeof(IEnumerator<T>), "enumerator");
?
Of course, the example here is contrived and doesn't have a practical use yet, but I'm trying to get the hang of expression trees that have bodies, in order to dynamically combine them at runtime in the future.
EDIT: My initial problem was solved by Alexandra, thanks! Of course, I've run into the next problem now. I've declared a BlockExpression
that has a variable in it. Inside that expression, I want another expression that references that variable. But I don't have an actual reference to that variable, just its name, because the expression is supplied externally.
var param = Expression.Variable(typeof(IEnumerable<T>), "something");
var block = Expression.Block(
new [] { param },
body
);
The body
variable is passed in externally and has no direct reference to param
, but does know the name of the variable in the expression ("something"
). It looks like this:
var body = Expression.Call(typeof(Console).GetMethod("WriteLine",new[] { typeof(bool) }),
Expression.Equal(Expression.Parameter(typeof(IEnumerable<int>), "something"), Expression.Constant(null)));
This is the "code" that this generates:
.Lambda #Lambda1<System.Action`1[System.Collections.Generic.IEnumerable`1[System.Int32]]>(System.Collections.Generic.IEnumerable`1[System.Int32] $something)
{
.Block(System.Collections.Generic.IEnumerable`1[System.Int32] $something) {
.Call System.Console.WriteLine($something== null)
}
}
However, it doesn't compile. With the same error as before.
TLDR: How do I reference a variable by identifier in an expression tree?
Each node in an expression tree is an expression. For example, an expression tree can be used to represent mathematical formula x < y where x, < and y will be represented as an expression and arranged in the tree like structure. Expression tree is an in-memory representation of a lambda expression.
Expression trees represent code in a tree-like data structure, where each node is an expression, for example, a method call or a binary operation such as x < y . You can compile and run code represented by expression trees.
Expression Trees provide richer interaction with the arguments that are functions. You write function arguments, typically using Lambda Expressions, when you create LINQ queries. In a typical LINQ query, those function arguments are transformed into a delegate the compiler creates.
You problem is that you didn't pass parameters and variables to your block expression. You use them in the "inner" expressions, but the block expression knows nothing about them. Basically, all you need to do is to pass all your parameters and variables to a block expression.
var @foreach = Expression.Block(
new ParameterExpression[] { item, enumerator, param },
assignToEnum,
Expression.Loop(
Expression.IfThenElse(
Expression.NotEqual(doMoveNext, Expression.Constant(false)),
assignCurrent,
Expression.Break(@break))
, @break)
);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With