Объединить несколько выражений LINQ из массива - PullRequest
2 голосов
/ 02 октября 2008

Я пытаюсь объединить список функций следующим образом.

У меня есть это:

Func<int, bool>[] criteria = new Func<int, bool>[3];
criteria[0] = i => i % 2 == 0;
criteria[1] = i => i % 3 == 0;
criteria[2] = i => i % 5 == 0;

И я хочу это:

Func<int, bool>[] predicates = new Func<int, bool>[3];
predicates[0] = i => i % 2 == 0;
predicates[1] = i => i % 2 == 0 && i % 3 == 0;
predicates[2] = i => i % 2 == 0 && i % 3 == 0 && i % 5 == 0;

Пока у меня есть следующий код:

Expression<Func<int, bool>>[] results = new Expression<Func<int, bool>>[criteria.Length];

for (int i = 0; i < criteria.Length; i++)
{
    results[i] = f => true;
    for (int j = 0; j <= i; j++)
    {
        Expression<Func<int, bool>> expr = b => criteria[j](b);
        var invokedExpr = Expression.Invoke(
            expr, 
            results[i].Parameters.Cast<Expression>());
        results[i] = Expression.Lambda<Func<int, bool>>(
            Expression.And(results[i].Body, invokedExpr), 
            results[i].Parameters);
    }
}
var predicates = results.Select(e => e.Compile()).ToArray();

Console.WriteLine(predicates[0](6)); // Returns true
Console.WriteLine(predicates[1](6)); // Returns false
Console.WriteLine(predicates[2](6)); // Throws an IndexOutOfRangeException

Кто-нибудь знает, что я делаю не так?

Ответы [ 2 ]

5 голосов
/ 02 октября 2008

Не нужно тянуть в выражения ...

    Func<int, bool>[] criteria = new Func<int, bool>[3];
    criteria[0] = i => i % 2 == 0;
    criteria[1] = i => i % 3 == 0;
    criteria[2] = i => i % 5 == 0;

    Func<int, bool>[] predicates = new Func<int, bool>[3];

    predicates[0] = criteria[0];
    for (int i = 1; i < criteria.Length; i++)
    {
        //need j to be an unchanging int, one for each loop execution.
        int j = i;

        predicates[j] = x => predicates[j - 1](x) && criteria[j](x);
    }

    Console.WriteLine(predicates[0](6)); //True
    Console.WriteLine(predicates[1](6)); //True
    Console.WriteLine(predicates[2](6)); //False
4 голосов
/ 02 октября 2008

Это было предположение, поскольку я мало что знаю об этом, но, похоже, это исправляет:

Func<int, bool>[] criteria = new Func<int, bool>[3]; 
criteria[0] = i => i % 2 == 0; 
criteria[1] = i => i % 3 == 0; 
criteria[2] = i => i % 5 == 0;
Expression<Func<int, bool>>[] results = new Expression<Func<int, bool>>[criteria.Length];
for (int i = 0; i < criteria.Length; i++)
{
    results[i] = f => true; 
    for (int j = 0; j <= i; j++)
    {
        int ii = i;
        int jj = j;
        Expression<Func<int, bool>> expr = b => criteria[jj](b); 
        var invokedExpr = Expression.Invoke(expr, results[ii].Parameters.Cast<Expression>()); 
        results[ii] = Expression.Lambda<Func<int, bool>>(Expression.And(results[ii].Body, invokedExpr), results[ii].Parameters);
    }
} 
var predicates = results.Select(e => e.Compile()).ToArray(); 

Ключом является введение 'ii' и 'jj' (возможно, имеет значение только одно, я не пробовал). Я думаю, что вы захватываете изменяемую переменную внутри лямбды, и, таким образом, когда вы, наконец, ссылаетесь на нее, вы видите измененное позднее значение, а не исходное значение.

...