针对每个循环处理跳过迭代的控件的问题,我发现允许在不断变化的集合上进行迭代:
例如,以下内容:
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:集合已修改;枚举操作可能无法执行。
然而,在a。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();
}
它跳过元素,因为迭代器运行在更改的集合上,而不会引发异常
错误?如果基础集合发生变化,迭代器是否需要抛出InvalidOPationException
?
所以我的问题是为什么对不断变化的ControlCollection
的迭代不会抛出Invalid术异常?
附录:
IEnumerator的文档说明:
枚举器没有对集合的独占访问权;因此,枚举集合本质上不是线程安全的过程。即使集合被同步,其他线程仍然可以修改集合,这会导致枚举器抛出异常。
在对foreach控件c#跳过控件的注释中也提出了同样的未引发异常的问题。该问题使用类似的代码,除了在调用Displace()
之前从控件
中显式删除子Control
...
foreach (Control cntrl in Controls)
{
if (cntrl.GetType() == typeof(Button))
{
Controls.Remove(cntrl);
cntrl.Dispose();
}
}
我仅通过文档就可以找到这种行为的解释。基本上,在枚举时修改任何集合都会导致抛出异常,这是一种错误的假设;这样的修改会导致未定义的行为,具体的集合类将决定如何处理该场景(如果有的话)。
根据IEnumerable的注释。GetEnumerator()和IEnumerable
如果对集合进行了更改,例如添加、修改或删除元素,则枚举器的行为未定义。
类,如字典
只要集合保持不变,枚举数就仍然有效。如果对集合进行了更改,例如添加、修改或删除元素,则枚举数将不可恢复地无效,并且下一次调用MoveNext或IENumator。重置会抛出Invalid术异常。
值得注意的是,正是我上面提到的每个类,而不是它们都实现的接口,通过“代码”指定了显式失败的行为。因此,它是否异常失败取决于每个类。
较旧的集合类,例如ArrayList和Hashtable,专门将此场景中的行为定义为未定义的,超出了要失效的枚举数。。。
只要集合保持不变,枚举数就保持有效。如果对集合进行了更改,例如添加、修改或删除元素,则枚举器将不可恢复地失效,并且其行为未定义。
...虽然在测试中我发现这两个类的枚举数都会在无效后抛出一个invalidooperationexception。
与上述类不同,控件。ControlCollection类既不定义也不评论此类行为,因此上述代码“仅”以微妙、不可预测的方式失败,无一例外地明确表示失败;它从未说过它会明确失败。
因此,一般来说,在枚举期间修改集合保证(可能)失败,但不保证引发异常。
这个问题的答案可以在的参考源代码中找到。
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()
中的注释,这些注释明确说明了这一点。
在我看来,这是一个误入歧途的“修复”,因为它通过引入一个微妙的错误来掩盖一个明显的错误(元素被悄悄地跳过,正如OP所指出的)。
下面是我的代码。当我运行它时,我在线程“main”java.lang.IndexOutOfBoundsException:Index:3、Size:2中得到异常,而不是我的异常消息。谁能解释一下我做错了什么,为什么会这样?谢谢!
问题内容: 我陷入了这个非常奇怪的问题。在客户端中,我传递的对象是 在服务器中,我正在读取对象 我的问题是为什么我会出现EOF异常。我对对象输入流的理解是,当我调用readObject()时应该阻塞直到获得对象,这样才能知道是否达到了eof?请帮忙! 这就是我创建对象流的方式 另外,在我写完对象并刷新之后,我应该关闭流。我不会关闭它,因为对象是从代码的不同部分开始一个接一个地定期编写的。 问题答案
问题内容: 我睡眠不足吗?以下代码 抛出此错误: 这绝对是行不通的!有人可以解释一下吗? 问题答案: 在规格中它说: 每当调用其origin-clean标志设置为false的canvas元素的toDataURL()方法时,该方法都必须引发SECURITY_ERR异常。 如果图像来自其他服务器,我认为您不能使用toDataURL()
null 为简洁起见,排除了getter和setter(用于所有字段)以及toString()。 我尽了最大的努力按照指导原则格式化代码,但它没有发生,请耐心等待。 @Entity@Table(name=“Products”)@XmlRootElement@NamedQueries({@NamedQuery(name=“Product.FindAll”,query=“从产品p中选择p”),@nam
在try块中为未抛出异常子类的方法捕获异常,将无法编译。当我捕捉到异常时,它起作用了。它是如何工作的??
问题内容: 直接的答案是因为s接口被指定为不会引发异常。但是为什么呢? 或换句话说:我必须依赖可以引发异常的函数。从理论上讲,这不应该发生。但是,如果发生这种情况,我希望它脱离我正在使用的整个函数(在中)。即我希望它的行为就像发生未处理的异常一样。 似乎这不可能以一种显而易见的自然方式进行(因为如果接口说它不能抛出异常,就不会)。 我该如何解决?用丑陋的try / catch并打印出异常,并希望我