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

时间:2011-04-10 06:05:50

标签: 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}
};  

背景

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

例如:在标题搜索文本框中:+(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);
}

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

第2阶段

现在上面的过滤器类型为Func&lt; Book ,bool&gt;。该类将是一个实体框架生成的类,我不想在我的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本书中的四本......除了标题为“恩德的游戏”的书外。那不对......

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

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

1 个答案:

答案 0 :(得分:4)

由于我们在这里使用LINQ to Objects,您应该可以使用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));
}