C# 这是Cast的正常行为吗<;T>;关于控件集合?

C# 这是Cast的正常行为吗<;T>;关于控件集合?,c#,winforms,C#,Winforms,嗨,伙计们,我发现了一个我无法逻辑解释的问题。在以下代码段中,flpRecordIndexes是一个FlowLayoutPabel,其中包含大量RecordIndexControl(我创建的用户控件)。我想删除除第一个控件以外的所有内容。flpRecordContainer也有同样的想法 如果我执行这个(没有ToList调用),它只删除一半的控件,例如,如果它是一个序列,它将删除(2,4,6,8)等等 foreach(flpRecordIndexes.Controls.Cast()中的var r

嗨,伙计们,我发现了一个我无法逻辑解释的问题。在以下代码段中,flpRecordIndexes是一个FlowLayoutPabel,其中包含大量RecordIndexControl(我创建的用户控件)。我想删除除第一个控件以外的所有内容。flpRecordContainer也有同样的想法

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

foreach(flpRecordIndexes.Controls.Cast()中的var recordIndexControl.Skip(1))
{
flprecordixes.Controls.Remove(recordIndexControl);
}
foreach(flpRecordContainer.Controls.Cast()中的var 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);
}
foreach(flpRecordIndexes.Controls.Cast().ToList().Skip(1)中的var recordIndexControl)
{
flprecordixes.Controls.Remove(recordIndexControl);
}
foreach(flpRecordContainer.Controls.Cast().ToList().Skip(1)中的var recordControl)
{
flpRecordContainer.Controls.Remove(recordControl);
}

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

这是完全正常的,您正在修改使用控件进行迭代的集合。Remove()调用。控件集合的行为与其他框架集合不同,执行此操作时不会引发异常。因此,实际上,根据混合情况,您将删除其他每个控件

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

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

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

调用
ToList()
会具体化集合,而
Cast
不会。一旦调用了
ToList()
,可以说列表是固定的,并且列表中有一个有限的数字

我建议通过
for
循环而不是
foreach
来迭代。这将完全避免您所看到的问题,实际上更有效。
ControlCollection
类继承了
IList
,因此您应该对它很在行

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

请注意
索引>=1
,以确保我们保留列表中的第一个控件。

您正在使用foreach修改列表,这就是它丢失某些控件的原因。ToList正在处理列表的副本。通常情况下,集合将抛出异常,表示该集合在枚举时已被修改<代码>控制收集不这样做。它允许删除项目。我总是使用
Skip(1).ToList()
而不是
.ToList().Skip(1)
。前者生成一个列表,该列表也可以通过索引访问,是内存中的集合。后者生成一个使用延迟执行的LINQ查询,因此如果不立即使用它,它会一次又一次地执行(就像您所做的那样)。值得注意的是,
.Skip(1).ToList()
应该优先于
.ToList().Skip(1)
。只适用于反向循环;)我真的很喜欢这个想法,但我不能给出一个以上的答案,你有我的投票权。你可以随时更改被接受的答案-最终由你选择,无论哪个答案最能解决你的问题。
for (var index = Controls.Count - 1; index >= 1; -- index)
{
    flpRecordContainer.Controls.RemoveAt(index);
}