我有一个linq查询,其结果我在foreach循环中迭代.
第一个是从表布局面板中获取控件集合的查询,然后迭代集合并从tableLayoutPanel中删除控件:
var AllItems = (from Item in this.tableLayoutPanel1.Controls.OfType<ItemControl>()
select Item);
foreach (ItemControl item in AllItems)
{
Trace.WriteLine("Removing " + item.ToString());
this.tableLayoutPanel1.Controls.Remove(item);
item.Dispose();
}
Run Code Online (Sandbox Code Playgroud)
上面没有按照我的预期进行(即抛出一个错误),它只删除了一半的控件(ODD编号为1),看起来每次迭代时AllItems都会减少它的自身,虽然底层集合被修改但没有错误是抛出.
如果我用一串字符串做同样的话:
string[] strs = { "d", "c", "A", "b" };
List<string> stringList = strs.ToList();
var allitems = from letter in stringList
select letter;
foreach (string let in allitems)
{
stringList.Remove(let);
}
Run Code Online (Sandbox Code Playgroud)
这次Visual Studio抛出一个错误(正如预期的那样)抱怨底层集合已经改变.
为什么第一个例子不会爆炸呢?
有一些关于Iterators/IEnumerable的东西,我在这里不理解,我想知道是否有人可以帮助我了解linq和foreach引擎盖下发生了什么.
(我知道我可以通过AllItems.ToList()解决这两个问题;在迭代之前,但是想要理解为什么第二个例子抛出错误而第一个没有
Ree*_*sey 10
为什么第一个例子不会爆炸呢?
其中的IEnumerator实现tableLayoutPanel1.Controls没有执行适当的检查以查看集合是否已被修改.这并不意味着它是一个安全的操作(因为结果不合适),而是该类没有以引发异常的方式实现(因为它可能应该).
枚举List<T>和删除项目时引发的异常实际上是由List<T>.Enumerator类型引发的,并不是"通用"语言功能.
请注意,这是该类型的一个很好的功能List<T>.Enumerator,因为IEnumerator<T>文档明确指出:
只要集合保持不变,枚举器仍然有效.如果对集合进行了更改(例如添加,修改或删除元素),则枚举数将无法恢复,并且其行为未定义.
虽然没有合同要求抛出异常,但当您进入"未定义行为"领域时,实现这样做是有益的.
在这种情况下,TableLayoutControlCollection类枚举器实现不执行额外检查,因此您将看到当您使用"行为未定义"的功能时会发生什么.(在这种情况下,它会删除所有其他控件.)
| 归档时间: |
|
| 查看次数: |
1858 次 |
| 最近记录: |