为什么ControlCollection不会抛出InvalidOperationException?

Mei*_*hes 11 .net c# foreach invalidoperationexception winforms

按照这个问题Foreach循环处理控件跳过迭代它告诉我,迭代是允许通过更改集合:

例如,以下内容:

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);
}
Run Code Online (Sandbox Code Playgroud)

InvalidOperationException:Collection已被修改; 枚举操作可能无法执行.

但是,在.Net表单中,您可以执行以下操作:

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();
}
Run Code Online (Sandbox Code Playgroud)

它会跳过元素,因为迭代器会在更改的集合上运行,而不会抛出异常

错误?InvalidOperationException 如果底层集合发生变化,是否需要抛出迭代器?

所以我的问题是为什么迭代改变ControlCollectionNOT抛出InvalidOperationException?

附录:

对文件IEnumerator说:

枚举器没有对集合的独占访问权限; 因此,枚举通过集合本质上不是一个线程安全的过程.即使集合是同步的,其他线程仍然可以修改集合,这会导致枚举器抛出异常.

Mat*_*son 9

答案可以在参考资料中找到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];
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

请特别注意MoveNext()明确解决此问题的评论.

IMO这是一个被误导的"修复",因为它通过引入一个微妙的错误掩盖了一个明显的错误(如OP所指出的那样,元素被默默地跳过).