为什么这三个LINQ代码产生不同(或错误)的结果?

For*_*ter 5 c# linq enumeration

以下是一些示例数据:

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}
};  
Run Code Online (Sandbox Code Playgroud)

背景:

以上使用Book类的简化版本.当然,它包含许多领域.我的最终目标是允许用户执行"高级"搜索,允许用户指定任何字段,并进一步允许用户使用布尔代数为特定字段指定关键字.

例如:在标题搜索文本框中:+(蛋糕|糕点)+〜恶魔

以上意思是:找到标题中有"蛋糕"或"糕点"字样的所有书籍,并且没有"恶魔"这个词.

问题:

宝贝步骤将导致最终的解决方案.所以我最初有以下代码:

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);
}
Run Code Online (Sandbox Code Playgroud)

上面的代码工作正常.它会在标题中查找包含"和'我'的书籍.

阶段2

现在上面的过滤器类型为Func < Book,bool>.该类将是一个实体框架生成的类,我不想在我的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"),
};
Run Code Online (Sandbox Code Playgroud)
//This works...
for (int i = 0; i != filters.Count; ++i)
{
    Func<string, bool> daF = filters[i];
    q = q.Where(b => (daF(b.Title)));
}     
Run Code Online (Sandbox Code Playgroud)
            //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)));
//            }
Run Code Online (Sandbox Code Playgroud)
            //This runs but doesn't produce the proper output
//            foreach (Func<string, bool> filter in filters)
//              q = q.Where(b => filter(b.Title));
Run Code Online (Sandbox Code Playgroud)
foreach (Book b in q)
{
    Console.WriteLine("Title:\t\t{0}\nPages:\t\t{1}\n",
                      b.Title, b.Pages);
}
Run Code Online (Sandbox Code Playgroud)

第一个注释掉的部分触发索引器超出范围异常,表明i的值为2.

第二个评论出来的作品运行并产生输出,但它打印出5本书中的四本......除了标题为"恩德的游戏"的书外.那是不对的...

所以,阅读我的帖子,我发现我无法记住解释每一个细节的坏习惯......

你去吧 请解释为什么不同的输出.我想你可能暗示我目前的"解决方案"可能有所改进.

Jef*_*ado 4

由于我们在这里使用 LINQ to Objects,因此您应该能够使用All(). 那么你就不需要循环了。

var query = books.Where(book => filters.All(filter => filter(book.Title)));
Run Code Online (Sandbox Code Playgroud)

这相当于:

var query = from book in books
            where filters.All(filter => filter(book.Title))
            select book;
Run Code Online (Sandbox Code Playgroud)

至于为什么其他尝试不起作用,您正在关闭循环变量。通常,因此在循环中使用 lambda 函数时应该小心。简单的修复方法是声明一个在 lambda 中使用的单独变量。请注意,您实际上在第一个查询中间接执行了此操作。但是,您根本不需要循环,而应该使用上面的查询之一。

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));
}
Run Code Online (Sandbox Code Playgroud)