Ausdrucks- / Anweisungsbäume

.net c# expression-trees

Frage

Aktualisierte Frage weiter unten

Ich habe in .NET 4 mit Ausdrucksbäumen experimentiert, um zur Laufzeit Code zu generieren, und ich habe versucht, die foreach Anweisung durch foreach einer Ausdrucksbaumstruktur zu implementieren.

Am Ende sollte der Ausdruck in der Lage sein, einen Delegaten zu generieren, der dies tut:

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

Ich habe die folgende Hilfsmethode entwickelt, die einen BlockExpression von einem IEnumerable generiert:

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

Der folgende Code:

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

Erzeugt diesen Ausdrucksbaum:

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

Dies scheint in Ordnung zu sein, aber der Aufruf von Compile für diesen Ausdruck führt zu einer Ausnahme:

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

Habe ich das hier nicht definiert:

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

?

Natürlich ist das Beispiel hier erfunden und hat noch keinen praktischen Nutzen, aber ich versuche, den Ausdruck von Bäumen zu bekommen, die Körper haben, um sie dynamisch zur Laufzeit in der Zukunft zu kombinieren.


EDIT: Mein erstes Problem wurde von Alexandra gelöst, danke! Natürlich bin ich jetzt auf das nächste Problem gestoßen. Ich habe eine BlockExpression deklariert, die eine Variable enthält. Innerhalb dieses Ausdrucks möchte ich einen anderen Ausdruck, der auf diese Variable verweist. Aber ich habe keine tatsächliche Referenz auf diese Variable, nur ihren Namen, weil der Ausdruck extern geliefert wird.

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

Die body Variable wird extern übergeben und hat keinen direkten Bezug auf param , kennt aber den Namen der Variablen im Ausdruck ( "something" ). Es sieht aus wie das:

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

Dies ist der "Code", den dies erzeugt:

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

Es kompiliert jedoch nicht. Mit dem gleichen Fehler wie zuvor.

TLDR: Wie referenziere ich eine Variable nach Bezeichner in einem Ausdrucksbaum?

Akzeptierte Antwort

Ihr Problem besteht darin, dass Sie Parameter und Variablen nicht an Ihren Blockausdruck übergeben haben. Sie verwenden sie in den "inneren" Ausdrücken, aber der Blockausdruck weiß nichts über sie. Im Grunde müssen Sie lediglich alle Parameter und Variablen an einen Blockausdruck übergeben.

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

Beliebte Antwort

Vergessen Sie nicht, IEnumerator in try / finally zu platzieren - viel Code (wie File.ReadLines ()) hängt davon ab.




Lizenziert unter: CC-BY-SA with attribution
Nicht verbunden mit Stack Overflow
Ist diese KB legal? Ja, lerne warum
Lizenziert unter: CC-BY-SA with attribution
Nicht verbunden mit Stack Overflow
Ist diese KB legal? Ja, lerne warum