I have a usercontrol with an internal list which I have exposed publically by implementing IEnumerable. When I use foreach to enumerate over it, the usercontrol gets disposed. Why is this happening?
Example to reproduce:
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 开发者_如何学Go(string s in vc)
Console.WriteLine(s);
}
[STAThread]
static void Main()
{
Application.Run(new Vanish());
}
}
Run it in the debugger and click the black square.
One of the interfaces implemented by IEnumerator
is IDisposable
. The foreach
loop will call Dispose
on the source of the loop once it's done processing the items.
A much better solution would be to factor your UserControl
into 2 parts
- The
UserControl
minus the enumerable interfaces - A property / method which exposes the collection it contains
For example
public class VanishingControl : UserControl
{
string[] strings = { "uno", "due", "tres" };
public IEnumerable<string> GetItems() {
foreach (var current in strings) {
yield return current;
}
}
}
Because your control is its own enumerator :-)
The enumerator is disposed after foreach
automatically.
I would suggest making the enumerator into a separate class. Or, alternatively, just use a standard collection as a member, and not make the control to be IEnumerable
itself.
This is the cause:
public IEnumerator GetEnumerator()
{
return this;
}
One of the IEnumerator
interface is IDisposable
, the foreach loop at the end of work invoke the Dispose
method.
You should provide new IEnumerator each time the GetEnumerator method is called and avoid to use both interfaces in one implementation. This is done by LINQ by but this is specific situtation.
Instead of IEnumerator
in the class VanishingControl
you should have a subclass that implement the IEnumerator
and in GetEnumeratiorn the new instace of that class is returned.
精彩评论