简体   繁体   English

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

[英]Why is this code with PredicateBuilder not working?

Why is my list not returning anything? 为什么我的清单没有退回任何东西?

 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();
        }
    }

I am using Joseph Albahari's PredicateBuilder. 我正在使用Joseph Albahari的PredicateBuilder。

You need to assign the result to your predicate variable: 您需要将结果分配给predicate变量:

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

The PredicateBuilder page also emphasizes an important issue: PredicateBuilder页面还强调了一个重要问题:

The temporary variable in the loop is required to avoid the outer variable trap, where the same variable is captured for each iteration of the foreach loop. 循环中的临时变量需要避免外部变量陷阱,其中为foreach循环的每次迭代捕获相同的变量。

Thus, your code should resemble this: 因此,您的代码应该类似于:

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

There are two issues here: 这里有两个问题:

  1. The Or extension-method does not mutate an existing expression-tree - it returns a new one. Or extension-method不会改变现有的表达式树 - 它会返回一个新的表达式树。 Expression-trees in general are immutable. 表达式树通常是不可变的。
  2. You are closing over the loop variable. 您正在关闭循环变量。 To understand why this is a problem, see Closing over the loop variable considered harmful . 要了解这是一个问题的原因,请参阅关闭被认为有害的循环变量

Try: 尝试:

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

Or even nicer: 甚至更好:

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

This doesn't look right to me: 这对我来说不合适:

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

I suspect it should be: 我怀疑它应该是:

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

In other words, just like the rest of LINQ, PredicateBuilder doesn't let you change the current predicate - it lets you build a new predicate based on the existing one and a new condition. 换句话说,就像LINQ的其余部分一样, PredicateBuilder不允许您更改当前谓词 - 它允许您基于现有谓词和新条件构建谓词。

That's certainly what the sample code suggests, anyway... 无论如何,这肯定是示例代码所暗示的......

When you look at the complete source code for PredicateBuilder (on the same page), that confirms it - the predicate is actually just an Expression<Func<T, bool>> - ie an expression tree. 当您查看PredicateBuilder的完整源代码(在同一页面上)时,它确认了它 - 谓词实际上只是一个Expression<Func<T, bool>> - 即表达式树。 You're not creating an instance of a PredicateBuilder class or anything like that. 您不是要创建PredicateBuilder类的实例或类似的东西。 Expression trees are immutable, so the only thing Or can do is return a new expression tree, like this: 表达式树是不可变的,所以唯一的事情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);
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM