为什么使用PredicateBuilder的代码不起作用?

时间:2011-03-14 17:54:21

标签: c# linq

为什么我的列表没有返回任何内容?

 class Program
    {
        static void Main(string[] args)
        {
            var list = new List<string>{ "apple" , "mango" , "chickoo", "kiwi" };
            var searchWord = new List<string>{ "a" };

            var predicate = PredicateBuilder.False<string>();

            foreach(var word in searchWord)
            {
                predicate.Or(p => p.Contains(word));
            }

           var qry = list.Where(predicate.Compile());

           foreach (var item in qry)
           {
               Console.WriteLine(item);
           }

           Console.Read();
        }
    }

我正在使用Joseph Albahari的PredicateBuilder。

3 个答案:

答案 0 :(得分:18)

您需要将结果分配给predicate变量:

predicate = predicate.Or(p => p.Contains(word));

PredicateBuilder page也强调了一个重要问题:

  

循环中的临时变量是   要求避免外部变量   陷阱,其中相同的变量是   为每次迭代捕获   foreach循环。

因此,您的代码应该类似于:

        foreach(var word in searchWord)
        {
            var temp = word;
            predicate = predicate.Or(p => p.Contains(temp));
        }

答案 1 :(得分:5)

这里有两个问题:

  1. Or扩展方法不会改变现有的表达式树 - 它会返回一个新的表达式树。表达式树通常是不可变的。
  2. 您正在关闭循环变量。要了解这是一个问题,请参阅Closing over the loop variable considered harmful
  3. 尝试:

    foreach(var word in searchWord)
    {
        var wordCopy = word;
        predicate = predicate.Or(p => p.Contains(wordCopy));
    }
    

    甚至更好:

    var predicate = searchWord.Aggregate(PredicateBuilder.False<string>(),
                       (predSoFar, word) => predSoFar.Or(p => p.Contains(word)));
    

答案 2 :(得分:2)

这对我来说不合适:

foreach(var word in searchWord)
{
    predicate.Or(p => p.Contains(word));
}

我怀疑它应该是:

foreach(var word in searchWord)
{
    predicate = predicate.Or(p => p.Contains(word));
}

换句话说,就像LINQ的其余部分一样,PredicateBuilder不允许您更改当前谓词 - 它可以让您构建 new 谓词基于现有的和新的条件。

这肯定是sample code所暗示的,无论如何......

当您查看PredicateBuilder的完整源代码(在同一页面上),确认它时 - 谓词实际上只是Expression<Func<T, bool>> - 即表达式树。您没有创建PredicateBuilder类的实例或类似的任何内容。表达式树是不可变的,所以Or 唯一可以做的就是返回一个新的表达式树,如下所示:

public static Expression<Func<T, bool>> Or<T>
    (this Expression<Func<T, bool>> expr1,
     Expression<Func<T, bool>> expr2)
{
    var invokedExpr = Expression.Invoke(expr2,
                                        expr1.Parameters.Cast<Expression>());
    return Expression.Lambda<Func<T, bool>>
          (Expression.OrElse(expr1.Body, invokedExpr), expr1.Parameters);
}