使用字典时,Linq表现出奇怪的行为

4

我遇到了一个奇怪的Linq行为:使用两个看起来相同的Linq表达式,却得到了不同的结果!如果我循环一次,就会得到相同的结果,但在循环上面,却什么都找不到。

下面是代码:

        Dictionary<String, String> mainDico = new Dictionary<String, String>();
        mainDico.Add("key1", "value1");
        mainDico.Add("key2", "value2");

        List<Dictionary<String, String>> ls = new List<Dictionary<String, String>>();

        Dictionary<String, String> fistDico = new Dictionary<String, String>();
        fistDico.Add("key1", "value1");
        fistDico.Add("key2", "value2");

        Dictionary<String, String> secondDico = new Dictionary<String, String>();
        secondDico.Add("key1", "other");
        secondDico.Add("key2", "other");

        ls.Add(fistDico);
        ls.Add(secondDico);


        IEnumerable<Dictionary<String, String>> failQuery = from p in ls
                                                            select p;

        IEnumerable<Dictionary<String, String>> successfulQuery = from p in ls
                                                            select p;

        String[] items = new String[] { "key1","key2" }; // with one element it works

        foreach (var item in items)
        {
            String temp = mainDico[item];
            failQuery = failQuery.Where(p => p[item] == temp);
            successfulQuery = successfulQuery.Where(p => p[item] == mainDico[item]);
        }

        Console.WriteLine(successfulQuery.SingleOrDefault() != null);//True
        Console.WriteLine(failQuery.SingleOrDefault() != null);//False

我只是将你的代码复制粘贴进去,然后输出了true, true...... - L.B
1
我在三台电脑上尝试了一下:True,False(DOT.NET 4.0),并在您的评论后再次尝试。 - Akli
4
L.B.可能在使用C# 5。他们改变了处理闭包的方式:http://blogs.msdn.com/b/ericlippert/archive/2009/11/12/closing-over-the-loop-variable-considered-harmful.aspx - Eric Dahlvang
1个回答

5
问题在于你正在闭合循环变量。
有问题的代码段就在这里:
foreach (var item in items)
{
    String temp = mainDico[item];
    failQuery = failQuery.Where(p => p[item] == temp);
    successfulQuery = successfulQuery.Where(p => p[item] == mainDico[item]);
}

在第二种情况下,你正在创建一个闭包lambda,它关闭了item(并且也是第一种情况;你应该真正修复它),并且直到foreach循环结束后才评估查询。这意味着item始终是foreach循环中的最后一个项目,而不是当前项目。这可以通过创建一个新的本地变量来轻松解决,这就是你在第一种情况下所做的,这就是为什么它有效的原因。
这里有一个相关链接,讨论了更多细节。(您可以通过搜索“close over loop variable”找到更多信息)。
请注意,C# 5.0中已更改此项,因为它是混淆和错误的常见原因。(这可能是某些人无法重现问题的原因。)
值得注意的是,这与字典无关。在您的查询中,item 实际上始终是 foreach 中的最后一个项目,而不是当前项目,这就是为什么它会失败的原因。任何依赖于 item 是当前值的操作都不会按照您想要的方式执行。

感谢您的迅速和简洁的回复! - Akli

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接