Hans
Hans

Reputation: 2832

variable '' of type '' referenced from scope '', but it is not defined

Well, the following code is self-explaining; I want to combine two expressions into one using And operator. The last line causes rune-time the error:

Additional information: variable 'y' of type 'System.String' referenced from scope '', but it is not defined

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

Upvotes: 54

Views: 41676

Answers (4)

Will
Will

Reputation: 2468

Another case where you'll get this error is when using BlockExpression with local variables.

Expression.Block has an overload with a variables parameter, and you need to pass in your local variable references to this parameter:

Expression.Block(
    variables: new[] { myLocalVar1, myLocalVar2 },
    expressions: /* some collection of expressions that use myLocalVar1 and myLocalVar2 */
);

Upvotes: 0

Hans
Hans

Reputation: 2832

Thanks everybody collaborated.

As @dasblinkenlight pointed out the two parameters in the two expressions are not the same. Reason? Well, it is the compiler trick. When compiling, it creates a class for each expression and name each parameter something like xxx1, xxx2,... completely different from the original names.

And the answer for .Net 4.0+:

How to Combine two lambdas

Upvotes: 20

CodeCaster
CodeCaster

Reputation: 151594

As indicated in the other answer, you have two expressions where both have a parameter named y. Those don't automatically relate to each other.

To properly compile your expression, you need to specify both source expression's parameters:

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

Of course, you'd want an expression that combines both expressions with only one parameter. You can rebuild the expressions like this:

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

As for your comment that you don't want to rebuild the expression, but do it on an existing expression's body and parameters: this works using ExpressionRewriter from Combining two lambda expressions in c# and AndAlso from Replacing the parameter name in the Body of an 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");

Upvotes: 28

Sergey Kalinichenko
Sergey Kalinichenko

Reputation: 726579

The problem is that parameter expression objects that represents variable y in expressions e1 and e2 are different. The fact that the two variables are named the same and have the same type does not matter: e1.Parameters.First() and e2.Parameters.First() is not the same object.

This causes the problem that you see: only e1's parameter y is available to Lambda<>, while e2's parameter y is out of scope.

To fix this problem use Expression APIs to create e1 and e2. This way you would be able to share the parameter expression across them, thus eliminating the problem of scope.

Upvotes: 24

Related Questions