这似乎应该得到回答,但我发现潜在的欺骗行为会提出不同的事情......
我注意到这似乎工作正常(sourceDirInclusion
是一个简单的Dictionary<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);
}
答案 0 :(得分:7)
简短回答:这不安全。
答案很长:来自IEnumerator<T>
documentation:
只要集合保持不变,枚举器仍然有效。如果对集合进行了更改,例如添加,修改或删除元素,则枚举数将无法恢复,并且其行为未定义。
请注意,文档说行为是未定义的,这意味着它可能有效,也可能不行。人们不应该依赖未定义的行为。
在这种情况下,它取决于Keys
可枚举的行为,关于它是否在您开始枚举时创建了键列表的副本。在这种特定情况下,我们从the docs知道Dictionary<,>.Keys
的返回值是一个引用回字典的集合:
返回的
中Dictionary<TKey, TValue>.KeyCollection
不是静态副本;相反,Dictionary<TKey, TValue>.KeyCollection
会引用原始Dictionary<TKey, TValue>
中的键。因此,对Dictionary<TKey, TValue>
的更改将继续反映在Dictionary<TKey, TValue>.KeyCollection
。
因此,在枚举字典的键时修改字典应该被认为是不安全的。
您可以通过一次更改来纠正此问题。改变这一行:
foreach (string dir in sourceDirInclusion.Keys)
对此:
foreach (string dir in sourceDirInclusion.Keys.ToList())
ToList()
扩展方法将创建密钥列表的显式副本,从而可以安全地修改字典; “基础馆藏”将是副本,而不是原件。
答案 1 :(得分:3)
如果将抛出
InvalidOperationException:Message =“Collection已被修改;枚举操作可能无法执行
避免将候选人添加到外部列表中。然后循环它并从目标容器(字典)中删除。
List<string> list = new List<string>(sourceDirInclusion.Keys.Count);
foreach (string dir in sourceDirInclusion.Keys)
{
if (sourceDirInclusion[dir] == null)
list.Add(dir);
}
foreach (string dir in list)
{
sourceDirInclusion.Remove(dir);
}
答案 2 :(得分:0)
检查一下:What is the best way to modify a list in a 'foreach' loop?
简而言之:
The collection used in foreach is immutable. This is very much by design.
正如MSDN上所说:
foreach语句用于遍历集合以获取所需的信息,但不能用于添加或删除源集合中的项目以避免不可预测的副作用。如果需要在源集合中添加或删除项目,请使用for循环。
<强>更新强> 您可以改为使用for循环:
for (int index = 0; index < dictionary.Count; index++) {
var item = dictionary.ElementAt(index);
var itemKey = item.Key;
var itemValue = item.Value;
}
答案 3 :(得分:0)
这是有效的,因为您正在遍历sourceDirInclusion.Keys。
但是,为了确保未来版本的FrameWork,我建议您在foreach语句中使用sourceDirInclusion.Keys.ToArray(),这样您就可以创建循环键的副本。
但这不起作用:
foreach(KeyValuePair<string, object> item in sourceDirInclusion)
{
if (item.Value == null)
sourceDirInclusion.Remove(item.Key);
}
通常,您无法在遍历集合时修改集合,但通常可以使用.ToArray()或.ToList()创建新集合,并在修改原始集合时遍历该集合。
祝你好运。