Eh bien, le code suivant s'explique de lui-même; Je veux combiner deux expressions en une en utilisant l'opérateur And
. La dernière ligne provoque l'erreur d'exécution:
Informations supplémentaires: variable 'y' de type 'System.String' référencée depuis la portée '', mais elle n'est pas définie
Code:
Expression<Func<string, bool>> e1 = y => y.Length < 100;
Expression<Func<string, bool>> e2 = y => y.Length < 200;
var e3 = Expression.And(e1.Body, e2.Body);
var e4 = Expression.Lambda<Func<string, bool>>(e3, e1.Parameters.ToArray());
e4.Compile(); // <--- causes run-time error
Merci, tout le monde a collaboré.
Comme l'a souligné @dasblinkenlight, les deux paramètres des deux expressions ne sont pas identiques. Raison? Eh bien, c'est l'astuce du compilateur. Lors de la compilation, il crée une classe pour chaque expression et nomme chaque paramètre quelque chose comme xxx1, xxx2, ... complètement différent des noms d'origine.
Et la réponse pour .Net 4.0+:
Le problème est que les objets d'expression de paramètre qui représentent la variable y
dans les expressions e1
Et e2
Sont différents. Le fait que les deux variables soient nommées de la même manière et aient le même type n'a pas d'importance: e1.Parameters.First()
et e2.Parameters.First()
n'est pas le même objet.
Cela provoque le problème que vous voyez: seul le paramètre de e1
y
est disponible pour Lambda<>
, Tandis que le paramètre de e2
y
est hors de portée.
Pour résoudre ce problème, utilisez les API Expression
pour créer e1
Et e2
. De cette façon, vous seriez en mesure de partager l'expression de paramètre entre eux, éliminant ainsi le problème de portée.
Comme indiqué dans l'autre réponse, vous avez deux expressions où les deux ont un paramètre nommé y
. Ceux-ci ne sont pas automatiquement liés les uns aux autres.
Pour compiler correctement votre expression, vous devez spécifier les deux paramètres de l'expression source:
Expression<Func<string, bool>> e1 = (y => y.Length > 0);
Expression<Func<string, bool>> e2 = (y => y.Length < 5);
var e3 = Expression.And(e1.Body, e2.Body);
// (string, string) by adding both expressions' parameters.
var e4 = Expression.Lambda<Func<string, string, bool>>(e3, new[]
{
e1.Parameters[0],
e2.Parameters[0]
});
Func<string, string, bool> compiledExpression = e4.Compile();
bool result = compiledExpression("Foo", "Foo");
Bien sûr, vous voudriez une expression qui combine les deux expressions avec un seul paramètre. Vous pouvez reconstruire les expressions comme ceci:
ParameterExpression param = Expression.Parameter(typeof(string), "y");
var lengthPropertyExpression = Expression.Property(param, "Length");
var e1 = Expression.GreaterThan(lengthPropertyExpression, Expression.Constant(0));
var e2 = Expression.LessThan(lengthPropertyExpression, Expression.Constant(5));
var e3 = Expression.AndAlso(e1, e2);
var e4 = Expression.Lambda<Func<string, bool>>(e3, new[] { param });
Func<string, bool> compiledExpression = e4.Compile();
bool result = compiledExpression("Foo");
Quant à votre commentaire que vous ne voulez pas reconstruire l'expression, mais faites-le sur le corps et les paramètres d'une expression existante: cela fonctionne en utilisant ExpressionRewriter
de Combinaison de deux expressions lambda en c # et AndAlso
from Remplacement du nom du paramètre dans le corps d'une expression :
Expression<Func<string, bool>> e1 = (y => y.Length > 0);
Expression<Func<string, bool>> e2 = (z => z.Length < 10);
var e3 = ParameterReplacer.AndAlso<string>(e1, e2);
Func<string, bool> compiledExpression = e3.Compile();
bool result = compiledExpression("Foo");