【问题标题】:Why do these three pieces of LINQ code produce different (or erroneous) results?为什么这三段 LINQ 代码会产生不同(或错误)的结果?
【发布时间】:2011-04-10 14:38:03
【问题描述】:

这里是一些示例数据:

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 类的简化版本。当然,它会包含许多字段。我的最终目标是允许用户执行“高级”搜索,允许用户指定 any 字段,并进一步允许用户使用布尔代数为特定字段指定关键字。

例如:在标题搜索文本框中:+ (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”的书籍。

第二阶段

现在上面的过滤器是 FuncBook, 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"),
};

//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 本书......除了标题为“ender's game”的书外,所有这些都打印出来。这不对……

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

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

【问题讨论】:

    标签: c# linq enumeration


    【解决方案1】:

    由于我们在这里使用 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 函数时应该小心。简单的解决方法是声明一个您在 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));
    }
    

    【讨论】:

    • 他们都是。这就是问题所在,:(
    猜你喜欢
    • 2016-02-08
    • 1970-01-01
    • 1970-01-01
    • 2016-11-13
    • 1970-01-01
    • 2016-07-23
    • 1970-01-01
    • 2018-04-19
    相关资源
    最近更新 更多