开发者

Why is my IEnumerable UserControl disposed after I enumerate over it with foreach?

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

  1. The UserControl minus the enumerable interfaces
  2. 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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜