这是控件集合中Cast <t>的正常行为吗?

时间:2016-03-14 16:04:45

标签: c# winforms

大家好我发现了一个我无法逻辑解释的问题。在下面的代码片段中,flpRecordIndexes是一个FlowLayoutPabel,它包含许多RecordIndexControl(我创建的用户控件)。我想删除除第一个控件之外的所有内容。与flpRecordContainer相同的想法。

如果我执行此操作(没有ToList调用),它只删除一半的控件,如果它是一个序列,例如它将删除(2,4,6,8)等。

foreach (var recordIndexControl in flpRecordIndexes.Controls.Cast<RecordIndexControl>().Skip(1))
{
     flpRecordIndexes.Controls.Remove(recordIndexControl);
}
foreach (var recordControl in flpRecordContainer.Controls.Cast<RecordControl>().Skip(1))
{
     flpRecordContainer.Controls.Remove(recordControl);
}

如果我执行此操作(使用ToList),它将删除除第一个控件之外的所有内容,即我想要的内容。

foreach (var recordIndexControl in    flpRecordIndexes.Controls.Cast<RecordIndexControl>().ToList().Skip(1))
{
      flpRecordIndexes.Controls.Remove(recordIndexControl);
}
foreach (var recordControl in flpRecordContainer.Controls.Cast<RecordControl>().ToList().Skip(1))
{
      flpRecordContainer.Controls.Remove(recordControl);
}

为什么在没有ToList的情况下调用Cast会产生这种行为?

2 个答案:

答案 0 :(得分:6)

这是完全正常的,您正在使用Controls.Remove()调用修改正在迭代的集合。 Controls集合的行为与其他框架集合不同,它在执行此操作时不会引发异常。所以实际上你会删除所有其他控件,具体取决于混音。

ToList()调用创建Controls集合的副本,它不再受Remove()调用的影响。这是正确的解决方法。

请记住,你很可能有一个讨厌的泄漏。您删除的控件必须处理。您不能再依赖Winforms为您执行此操作,因为它们不再位于Controls集合中。未能处理它们是永久性泄漏,垃圾收集器无法帮助。

答案 1 :(得分:2)

  

为什么在没有ToList的情况下调用Cast会产生这种行为?

调用ToList()实现了集合,而Cast<T>没有。一旦ToList()被调用,列表就会固化,所以你在列表中有一个有限的数字。

我建议通过for循环而不是foreach来迭代Control.Controls。这将避免您完全看到的问题,实际上更高效。 ControlCollection类会继承IList,因此您应该对此感到满意。

for (var index = Controls.Count - 1; index >= 1; -- index)
{
    flpRecordContainer.Controls.RemoveAt(index);
}

请注意index >= 1以确保我们将第一个控件留在列表中。