Деревья выражений / операторов

Обновленный вопрос дальше вниз

Я экспериментировал с деревьями выражений в .NET 4 для генерации кода во время выполнения, и я пытался реализоватьforeach утверждение путем построения дерева выражений.

В конце выражение должно иметь возможность генерировать делегат, который делает это:

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

Я придумал следующий вспомогательный метод, который генерирует BlockExpression из 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;

}

Следующий код:

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

Создает это дерево выражений:

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

Это вроде бы нормально, но звонюCompile в этом выражении выдается исключение:

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

Разве я не определил это здесь:

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

?

Конечно, пример здесь надуман и пока не имеет практического применения, но я пытаюсь получить представление о деревьях выражений, которые имеют тела, чтобы динамически комбинировать их во время выполнения в будущем.

РЕДАКТИРОВАТЬ: Моя первоначальная проблема была решена Александрой, спасибо! Конечно, я столкнулся с следующей проблемой сейчас. Я объявилBlockExpression в котором есть переменная. Внутри этого выражения я хочу другое выражение, которое ссылается на эту переменную. Но у меня нет фактической ссылки на эту переменную, только ее имя, потому что выражение предоставляется извне.

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

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

body переменная передается извне и не имеет прямой ссылки наparam, но знает имя переменной в выражении ("something"). Это выглядит так:

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

Это «код», который это генерирует:

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

Тем не менее, он не компилируется. С той же ошибкой, что и раньше.

TLDR: Как мне ссылаться на переменную по идентификатору в дереве выражений?

Ответы на вопрос(3)

Ваш ответ на вопрос