在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()
方法在这个意义上不会达到我的意图。
这样做是完全安全的,并且不应该有任何问题,因为您没有直接修改要迭代的集合。虽然你做了其他影响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
)不是被修改的集合(blacklist
或ret
)。
将发生的是,循环的每次迭代将根据查询谓词计算当前项,查询谓词将检查blacklist
集合以查看它是否包含当前项的键。这是惰性计算,因此在一次迭代中对blacklist
的更改将潜在地影响后续的迭代,但它不会是错误。(blacklist
在每次迭代时都被完全求值,它的枚举数不被保留。)