以下是一些示例数据:

List<Book> books = new List<Book>()
{
    new Book(){Title = "artemis fowl: the time paradox", Pages = 380},
    new Book(){Title = "the lieutenant", Pages = 258},
    new Book(){Title = "the wheel of time", Pages = 1032},
    new Book(){Title = "ender's game", Pages = 404},
    new Book(){Title = "the sphere",  Pages = 657}
};

背景
上面使用的是图书类的简化版本。当然,它会包含许多字段。我的最终目标是允许用户执行“高级”搜索,允许用户指定任何字段,并进一步允许用户使用布尔代数为特定字段指定关键字。
例如:在标题搜索文本框中:the+(cake pastry)+~ demon
上面的意思是:找到所有的书,在书名中,有“the”这个词,或者“cake”或者“pastry”,但是没有“demon”这个词。
问题:
小步骤将导致最终的解决方案。所以我最初有以下代码:
List<Func<Book, bool>> fs = new List<Func<Book, bool>>()
{
    b => b.Title.Contains("me"),
    b => b.Title.Contains("the")
};

var q2 = from b in books select b;
foreach (var f in fs)
    q2 = q2.Where(f);

foreach (Book b in q2)
{
    Console.WriteLine("Title:\t\t{0}\nPages:\t\t{1}\n",
                      b.Title, b.Pages);
}

上面的代码运行良好。它寻找书名中包含“the”和“me”的书籍。
第2阶段
现在上面的过滤器是func类型。这个类将是一个实体框架生成的类,我不想在我的ui层中使用,在那里搜索短语将被输入,搜索过滤器将被生成并传递给bll。
所以我有以下三种尝试:
var q = from b in books select b;

List<Func<string, bool>> filters  = new List<Func<string, bool>>()
{
    s => s.Contains("me"),
    s => s.Contains("the"),
};

//This works...
for (int i = 0; i != filters.Count; ++i)
{
    Func<string, bool> daF = filters[i];
    q = q.Where(b => (daF(b.Title)));
}

            //This produces an exception...
            //Due to index in query?
//            for (int i = 0; i != filters.Count; ++i)
//            {
//                q = q.Where(b => ((filters[i])(b.Title)));
//            }

            //This runs but doesn't produce the proper output
//            foreach (Func<string, bool> filter in filters)
//              q = q.Where(b => filter(b.Title));

foreach (Book b in q)
{
    Console.WriteLine("Title:\t\t{0}\nPages:\t\t{1}\n",
                      b.Title, b.Pages);
}

第一个注释掉的片段触发索引器超出范围异常,声明i的值为2。
第二个注释输出运行并产生输出,但它打印出5本书中的4本…除了《安德的游戏》这本书。那不对…
所以,翻阅我的文章,我发现我不能控制我解释每一个小细节的坏习惯…
那就这样。请解释为什么不同的输出。我想你可以暗示一下我目前的“解决方案”可能会有哪些改进。

最佳答案

因为我们在这里使用linq来处理对象,所以您应该能够使用All()。那你就不需要循环了。

var query = books.Where(book => filters.All(filter => filter(book.Title)));

相当于:
var query = from book in books
            where filters.All(filter => filter(book.Title))
            select book;

至于为什么其他尝试不起作用,你是closing over the loop variable。通常,在循环中使用lambda函数时应该小心,因为这一点。简单的修复方法是声明在lambdas中使用的单独变量。注意,您实际上是在第一个查询中间接地这样做的。但是,您根本不需要循环,应该使用上面的查询之一。
for (int i = 0; i != filters.Count; ++i)
{
    var index = i;
    q = q.Where(b => filters[index](b.Title));
}

foreach (Func<string, bool> f in filters)
{
    var filter = f;
    q = q.Where(b => filter(b.Title));
}

09-10 03:24
查看更多