为什么ControlCollection不抛出InvalidOperationException? [英] Why does ControlCollection NOT throw InvalidOperationException?
问题描述
遵循此问题用于处理跳过跳过迭代的控件的Foreach循环让我感到困惑的是,允许对变化的集合进行迭代:
Following this question Foreach loop for disposing controls skipping iterations it bugged me that iteration was allowed over a changing collection:
例如,以下内容:
List<Control> items = new List<Control>
{
new TextBox {Text = "A", Top = 10},
new TextBox {Text = "B", Top = 20},
new TextBox {Text = "C", Top = 30},
new TextBox {Text = "D", Top = 40},
};
foreach (var item in items)
{
items.Remove(item);
}
投掷
InvalidOperationException:集合已修改;枚举操作可能无法执行.
InvalidOperationException: Collection was modified; enumeration operation may not execute.
但是,您可以在.Net表单中执行以下操作:
However in a .Net Form you can do:
this.Controls.Add(new TextBox {Text = "A", Top = 10});
this.Controls.Add(new TextBox {Text = "B", Top = 30});
this.Controls.Add(new TextBox {Text = "C", Top = 50});
this.Controls.Add(new TextBox {Text = "D", Top = 70});
foreach (Control control in this.Controls)
{
control.Dispose();
}
跳过元素,因为迭代器运行在变化的集合上,没有引发异常
which skips elements because the the iterator runs over a changing collection, without throwing an exception
错误?如果底层集合发生变化,是否不需要迭代器抛出InvalidOperationException
?
bug? aren't iterators required to throw InvalidOperationException
if the underlaying collection changes?
所以我的问题是为什么在变化的ControlCollection
上进行迭代时不会抛出InvalidOperationException?
So my question is Why does iteration over a changing ControlCollection
NOT throw InvalidOperationException?
附录:
IEnumerator
的文档说:
The documentation for IEnumerator
says:
枚举器没有对该集合的独占访问权;因此,通过集合进行枚举本质上不是线程安全的过程.即使同步了集合,其他线程仍然可以修改该集合,这会导致枚举器抛出异常.
推荐答案
The answer to this can be found in the Reference Source for ControlCollectionEnumerator
private class ControlCollectionEnumerator : IEnumerator {
private ControlCollection controls;
private int current;
private int originalCount;
public ControlCollectionEnumerator(ControlCollection controls) {
this.controls = controls;
this.originalCount = controls.Count;
current = -1;
}
public bool MoveNext() {
// VSWhidbey 448276
// We have to use Controls.Count here because someone could have deleted
// an item from the array.
//
// this can happen if someone does:
// foreach (Control c in Controls) { c.Dispose(); }
//
// We also dont want to iterate past the original size of the collection
//
// this can happen if someone does
// foreach (Control c in Controls) { c.Controls.Add(new Label()); }
if (current < controls.Count - 1 && current < originalCount - 1) {
current++;
return true;
}
else {
return false;
}
}
public void Reset() {
current = -1;
}
public object Current {
get {
if (current == -1) {
return null;
}
else {
return controls[current];
}
}
}
}
要特别注意MoveNext()
中明确解决此问题的注释.
Pay particular attention to the comments in MoveNext()
which explicitly address this.
IMO这是一个误导的修复程序",因为它通过引入细微的错误掩盖了一个明显的错误(如OP所述,元素被悄悄地跳过了.)
IMO this is a misguided "fix" because it masks an obvious error by introducing a subtle one (elements are silently skipped, as noted by the OP).
这篇关于为什么ControlCollection不抛出InvalidOperationException?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!