Drzewa wyrażenia / oświadczenia

Zaktualizowane pytanie Dalej w dół

Eksperymentowałem z drzewami wyrażeń w .NET 4, aby wygenerować kod w czasie wykonywania i próbowałem zaimplementowaćforeach instrukcja budowania drzewa wyrażeń.

W końcu wyrażenie powinno być w stanie wygenerować delegata, który to robi:

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

Wymyśliłem następującą metodę pomocniczą, która generuje BlockExpression z 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;

}

Poniższy kod:

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

Generuje to drzewo wyrażeń:

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

To wydaje się być w porządku, ale dzwoniCompile w tym wyrażeniu powstaje wyjątek:

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

Czy nie zdefiniowałem tego tutaj:

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

?

Oczywiście przykład tutaj jest wymyślony i nie ma jeszcze praktycznego zastosowania, ale staram się opanować drzewa wyrażeń, które mają ciała, w celu dynamicznego łączenia ich w czasie wykonywania w przyszłości.

EDYTOWAĆ: Mój początkowy problem został rozwiązany przez Aleksandrę, dzięki! Oczywiście natrafiłem teraz na następny problem. OświadczyłemBlockExpression który ma w sobie zmienną. Wewnątrz tego wyrażenia chcę innego wyrażenia odwołującego się do tej zmiennej. Ale nie mam rzeczywistego odniesienia do tej zmiennej, tylko jej nazwę, ponieważ wyrażenie jest dostarczane zewnętrznie.

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

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

Thebody zmienna jest przekazywana zewnętrznie i nie ma bezpośredniego odniesienia doparam, ale zna nazwę zmiennej w wyrażeniu ("something"). To wygląda tak:

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

Jest to „kod”, który generuje:

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

Nie kompiluje się jednak. Z tym samym błędem co poprzednio.

TLDR: Jak odwołać się do zmiennej przez identyfikator w drzewie wyrażeń?

questionAnswers(3)

yourAnswerToTheQuestion