在foreach循环中,我应该期待一个错误,因为查询收集已经改变了吗?

时间:2011-08-24 14:56:48

标签: c# linq

例如

       var query = myDic.Where(x => !blacklist.Contains(x.Key));
        foreach (var item in query)
        {
            if (condition)
              blacklist.Add(item.key+1);  //key is int type
            ret.add(item);
        }
       return ret;

这段代码有效吗?以及如何改进它?

更新

我希望我的blacklist.add(item.key+1)会导致较小的ret,否则会导致ToList()。在这个意义上,{{1}}方法无法实现我的意图。 还有其他更好的想法,正确而明确。

3 个答案:

答案 0 :(得分:2)

是。迭代集合时,严禁更改集合内部对象。

<强>更新

我最初将此作为评论,但这里还有一些信息:

我应该注意到,我的知识来自我很久以前读过的经验和文章。您可以执行上面的代码,因为(我相信)查询包含对黑名单中所选对象的引用。黑名单可能会改变,但不能查询。如果你严格迭代黑名单,你将无法添加到黑名单集合。

答案 1 :(得分:2)

您提供的代码不会引发异常。正在迭代的集合(myDic)不是要修改的集合(blacklistret)。

循环的每次迭代都将针对查询谓词计算当前项,这将检查blacklist集合以查看它是否包含当前项的键。这是懒惰的评估,因此在一次迭代中对blacklist的更改可能会影响后续迭代,但这不会是错误。 (blacklist在每次迭代时都被完全评估,其枚举器未被保留。)

答案 2 :(得分:2)

这样做是完全安全的,不应该有任何问题因为你没有直接修改你正在迭代的集合。虽然你正在进行影响where子句的其他更改,但它不会让你感到沮丧。

对查询(如编写)进行了延迟评估,因此在迭代集合时会更新blacklist,并且所有后续迭代都会在迭代时看到列表中任何新添加的项目。

以上代码实际上与此相同:

foreach (var item in myDic)
{
    if (!blacklist.Contains(item.Key))
    {
        if (condition)
            blacklist.Add(item.key + 1);
    }
}

所以你应该得到的是,只要你没有直接修改你正在迭代的集合(in循环中foreach之后的项目),你是什么做得很安全。

如果您仍然不相信,请考虑这一点以及将向控制台写出的内容:

var blacklist = new HashSet<int>(Enumerable.Range(3, 100));
var query = Enumerable.Range(2, 98).Where(i => !blacklist.Contains(i));
foreach (var item in query)
{
    Console.WriteLine(item);
    if ((item % 2) == 0)
    {
        var value = 2 * item;
        blacklist.Remove(value);
    }
}