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

发布于 2024-10-21 18:42:46 字数 719 浏览 2 评论 0 原文

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

 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。

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

橘香 2024-10-28 18:42:46

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

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

PredicateBuilder 页面还强调了一个重要问题:

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

因此,您的代码应类似于以下内容:

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

You need to assign the result to your predicate variable:

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

The PredicateBuilder page also emphasizes an important issue:

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.

Thus, your code should resemble this:

        foreach(var word in searchWord)
        {
            var temp = word;
            predicate = predicate.Or(p => p.Contains(temp));
        }
多情出卖 2024-10-28 18:42:46

这里有两个问题:

  1. Or 扩展方法不会改变现有的表达式树 - 它返回一个新的表达式树。表达式树通常是不可变的。
  2. 您正在关闭循环变量。要了解此问题的原因,请参阅 关闭被认为有害的循环变量

尝试:

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

There are two issues here:

  1. The Or extension-method does not mutate an existing expression-tree - it returns a new one. 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)));
薄荷港 2024-10-28 18:42:46

这对我来说看起来不正确:

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 不允许您更改当前谓词- 它允许您基于现有谓词和新条件构建谓词。

无论如何,这肯定是 示例代码 所建议的......

当您查看完整的源代码时PredicateBuilder 的代码(在同一页上),证实了这一点 - 谓词实际上只是一个 Expression> - 即表达式树。您没有创建 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);
}

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.

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>> - i.e. an expression tree. You're not creating an instance of a PredicateBuilder class or anything like that. Expression trees are immutable, so the only thing Or can do is return a new expression tree, like this:

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);
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文