我有一个带有内部列表的usercontrol,我通过实现IEnumerable公开地公开了它。当我使用foreach对其进行枚举时,用户控件将被释放。为什么会这样?
重现的例子:
using System;
using System.Collections;
using System.Drawing;
using System.Windows.Forms;
public class VanishingControl : UserControl, IEnumerable, IEnumerator
{
string[] strings = { "uno", "due", "tres" };
int position = -1;
public IEnumerator GetEnumerator()
{
return this;
}
public object Current
{
get { return strings[position]; }
}
public bool MoveNext()
{
position++;
return position < strings.Length;
}
public void Reset()
{
position = 0;
}
protected override void Dispose(bool disposing)
{
Console.WriteLine("bye!");
base.Dispose(disposing);
}
}
public class Vanish : Form
{
private VanishingControl vc = new VanishingControl();
public Vanish()
{
vc.BackColor = Color.Black;
vc.Click += vc_Click;
Controls.Add(vc);
}
void vc_Click(object sender, EventArgs e)
{
foreach (string s in vc)
Console.WriteLine(s);
}
[STAThread]
static void Main()
{
Application.Run(new Vanish());
}
}
在调试器中运行它,然后单击黑色方块。
答案 0 :(得分:4)
因为你的控件是它自己的枚举器: - )
枚举器会在foreach
之后自动处理。
我建议将枚举器放入一个单独的类中。或者,或者,只使用标准集合作为成员,而不是使控件本身为IEnumerable
。
答案 1 :(得分:4)
IEnumerator
实施的其中一个界面是IDisposable
。一旦处理完项目,foreach
循环将在循环源上调用Dispose
。
更好的解决方案是将UserControl
分为两部分
UserControl
减去可枚举的接口例如
public class VanishingControl : UserControl
{
string[] strings = { "uno", "due", "tres" };
public IEnumerable<string> GetItems() {
foreach (var current in strings) {
yield return current;
}
}
}
答案 2 :(得分:2)
这是原因:
public IEnumerator GetEnumerator()
{
return this;
}
IEnumerator
接口之一是IDisposable
,工作结束时的foreach循环调用Dispose
方法。
每次调用GetEnumerator方法时都应该提供新的IEnumerator,并避免在一个实现中使用这两个接口。这是由LINQ完成的,但这是具体的情况。
而不是类IEnumerator
中的VanishingControl
,而是应该有一个实现IEnumerator
的子类,而在GetEnumeratiorn中,将返回该类的新实例。