在foreach循环中,由于查询集合已经更改,我应该期望出现错误吗?

本文关键字:我应该 期望 错误 集合 循环 foreach 于查询 查询 | 更新日期: 2023-09-27 18:06:44

例如

       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()方法在这个意义上不会达到我的意图。

在foreach循环中,由于查询集合已经更改,我应该期望出现错误吗?

这样做是完全安全的,并且不应该有任何问题,因为您没有直接修改要迭代的集合。虽然你做了其他影响where子句的更改,但它不会在你身上爆炸。

查询(如编写的那样)是惰性求值的,因此当您遍历集合时blacklist被更新,并且所有后续迭代将在迭代时看到列表中任何新添加的项。

上面的代码实际上与以下代码相同:

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

所以你应该从中得到的是,只要你不直接修改你要迭代的集合(foreach循环中in之后的项),你所做的是安全的。

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

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);
    }
}

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

我最初做了一个注释,但这里是进一步的信息:

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

您的代码不会抛出异常。被迭代的集合(myDic)不是被修改的集合(blacklistret)。

将发生的是,循环的每次迭代将根据查询谓词计算当前项,查询谓词将检查blacklist集合以查看它是否包含当前项的键。这是惰性计算,因此在一次迭代中对blacklist的更改将潜在地影响后续的迭代,但它不会是错误。(blacklist在每次迭代时都被完全求值,它的枚举数不被保留。)