Arbres d'expression / de déclaration

.net c# expression-trees

Question

Question mise à jour plus bas

J'ai expérimenté des arbres d'expression dans .NET 4 pour générer du code au moment de l'exécution et j'ai essayé d'implémenter l'instruction foreach en créant un arbre d'expression.

En fin de compte, l'expression devrait être capable de générer un délégué qui fait ceci:

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

J'ai mis au point la méthode d'assistance suivante qui génère une expression BlockExpression à partir d'un IEnumerable:

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

Le code suivant:

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

Génère cet arbre d'expression:

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

Cela semble être OK, mais appeler Compile sur cette expression entraîne une exception:

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

Je ne l'ai pas défini ici:

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

?

Bien entendu, l'exemple présenté ici est artificiel et n'a pas encore d'utilisation pratique, mais j'essaie de comprendre les arbres d'expression dotés de corps afin de les combiner de manière dynamique à l'avenir.


EDIT: Mon problème initial a été résolu par Alexandra, merci! Bien sûr, j'ai rencontré le prochain problème maintenant. J'ai déclaré une BlockExpression une variable. Dans cette expression, je veux une autre expression qui fait référence à cette variable. Mais je n'ai pas de référence réelle à cette variable, mais juste son nom, car l'expression est fournie en externe.

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

La variable body est transmise de manière externe et ne contient aucune référence directe à param , mais connaît le nom de la variable dans l'expression ( "something" ). Cela ressemble à ceci:

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

C'est le "code" que cela génère:

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

Cependant, il ne compile pas. Avec la même erreur qu'avant.

TLDR: Comment référencer une variable par identifiant dans un arbre d'expression?

Réponse acceptée

Votre problème est que vous n'avez pas passé de paramètres ni de variables à votre expression de bloc. Vous les utilisez dans les expressions "internes", mais l'expression de bloc ne les connaît pas. En gros, tout ce que vous avez à faire est de transmettre tous vos paramètres et variables à une expression de bloc.

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

Réponse populaire

N'oubliez pas de disposer d'IEnumerator dans try / finally - beaucoup de code (tel que File.ReadLines ()) en dépend.




Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Est-ce KB légal? Oui, apprenez pourquoi
Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Est-ce KB légal? Oui, apprenez pourquoi