Переменная '' типа '', ссылающаяся на область видимости '', но не определена

Ну, следующий код самообучающийся; Я хочу объединить два выражения в один, используя оператор And. Последняя строка вызывает ошибку rune-time:

Дополнительная информация: переменная 'y' типа 'System.String', ссылающаяся на область действия '', но не определена

код:

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

Ответ 1

Спасибо всем, кто сотрудничал.

Как указывал dasblinkenlight, два параметра в двух выражениях не совпадают. Причина? Ну, это трюк компилятора. При компиляции он создает класс для каждого выражения и называет каждый параметр, похожий на xxx1, xxx2,... полностью отличный от исходных имен.

И ответ для .Net 4.0 +:

Как объединить два лямбда

Ответ 2

Проблема заключается в том, что объекты выражения параметров, которые представляют переменную y в выражениях e1 и e2, различны. Тот факт, что две переменные называются одинаковыми и имеют один и тот же тип, не имеет значения: e1.Parameters.First() и e2.Parameters.First() - не тот же объект.

Это вызывает проблему: параметр e1 y доступен для Lambda<>, а параметр e2 y недоступен.

Чтобы устранить эту проблему, используйте API Expression для создания e1 и e2. Таким образом, вы сможете совместно использовать выражение параметров по ним, тем самым устраняя проблему области.

Ответ 3

Как указано в другом ответе, у вас есть два выражения, в которых оба параметра имеют параметр y. Они не связаны автоматически друг с другом.

Чтобы правильно скомпилировать ваше выражение, вам нужно указать оба параметра исходного выражения:

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

Конечно, вам нужно выражение, которое объединяет оба выражения только с одним параметром. Вы можете перестроить выражения следующим образом:

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

Что касается вашего комментария, что вы не хотите перестраивать выражение, но делаете это на существующей структуре выражения и параметрах: это работает с использованием ExpressionRewriter из Сочетание двух лямбда-выражений в С# и AndAlso из Замена имени параметра в кубе выражения:

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