Expresión / árboles de declaración

Pregunta actualizada más abajo

He estado experimentando con árboles de expresión en .NET 4 para generar código en tiempo de ejecución y he estado intentando implementar elforeach declaración mediante la construcción de un árbol de expresión.

Al final, la expresión debería poder generar un delegado que haga esto:

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

Se me ocurrió el siguiente método auxiliar que genera una expresión de bloque de un 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;

}

El siguiente código:

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

Genera este árbol de expresiones:

.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:
    }
}

Esto parece estar bien, pero llamandoCompile en esa expresión resulta en una excepción:

"variable 'enumerator' of type 'System.Collections.Generic.IEnumerator`1[System.Int32]' referenced from scope '', but it is not defined"

¿No lo definí aquí?

    var enumerator = Expression.Variable(typeof(IEnumerator<T>), "enumerator");

?

Por supuesto, el ejemplo aquí está diseñado y todavía no tiene un uso práctico, pero estoy tratando de entender los árboles de expresión que tienen cuerpos, para combinarlos dinámicamente en el tiempo de ejecución en el futuro.

EDITAR: Mi problema inicial fue resuelto por Alexandra, gracias! Por supuesto, me he encontrado con el siguiente problema ahora. He declarado unBlockExpression que tiene una variable en ella Dentro de esa expresión, quiero otra expresión que haga referencia a esa variable. Pero no tengo una referencia real a esa variable, solo su nombre, porque la expresión se suministra externamente.

var param = Expression.Variable(typeof(IEnumerable<T>), "something");

var block = Expression.Block(
                new [] { param },
                body
            );

losbody La variable se pasa externamente y no tiene referencia directa aparam, pero sabe el nombre de la variable en la expresión ("something"). Se parece a esto:

var body = Expression.Call(typeof(Console).GetMethod("WriteLine",new[] { typeof(bool) }), 
               Expression.Equal(Expression.Parameter(typeof(IEnumerable<int>), "something"), Expression.Constant(null)));

Este es el "código" que esto genera:

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

Sin embargo, no se compila. Con el mismo error que antes.

TLDR: ¿Cómo hago referencia a una variable por identificador en un árbol de expresión?

Respuestas a la pregunta(3)

Su respuesta a la pregunta