在C#中,当枚举容器被修改时,foreach的行为如何
作者:互联网
这似乎应该得到回答,但我发现潜在的欺骗行为正在问不同的事情……
我注意到这似乎工作正常(sourceDirInclusion是一个简单的字典< X,Y>)
foreach (string dir in sourceDirInclusion.Keys)
{
if (sourceDirInclusion[dir] == null)
sourceDirInclusion.Remove(dir);
}
这是否意味着从foreach中的集合中删除项目是安全的,还是我很幸运?
如果我在字典中添加更多元素而不是删除呢?
我试图解决的问题是sourceDirInclusion最初是填充的,但是每个值都可以在第二次传递中向字典贡献新项.例如,我想做的是:
foreach (string dir in sourceDirInclusion.Keys)
{
X x = sourceDirInclusion[dir];
sourceDirInclusion.Add(X.dir,X.val);
}
解决方法:
简短回答:这不安全.
答案很长:从IEnumerator<T>
documentation开始:
An enumerator remains valid as long as the collection remains unchanged. If changes are made to the collection, such as adding, modifying, or deleting elements, the enumerator is irrecoverably invalidated and its behavior is undefined.
请注意,文档说行为是未定义的,这意味着它可能有效,也可能不行.永远不要依赖未定义的行为.
在这种情况下,它取决于可枚举键的行为,关于它是否在您开始枚举时创建键列表的副本.在这种特定情况下,我们从the docs知道Dictionary<,> .Keys的返回值是一个引用回字典的集合:
The returned
Dictionary<TKey, TValue>.KeyCollection
is not a static copy; instead, theDictionary<TKey, TValue>.KeyCollection
refers back to the keys in the originalDictionary<TKey, TValue>
. Therefore, changes to theDictionary<TKey, TValue>
continue to be reflected in theDictionary<TKey, TValue>.KeyCollection
.
因此,在枚举字典的键时修改字典应该被认为是不安全的.
您可以通过一次更改来纠正此问题.改变这一行:
foreach (string dir in sourceDirInclusion.Keys)
对此:
foreach (string dir in sourceDirInclusion.Keys.ToList())
ToList()扩展方法将创建键列表的显式副本,从而可以安全地修改字典; “基础集合”将是副本而不是原始副本.
标签:c,net-2-0 来源: https://codeday.me/bug/20190718/1493917.html