Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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 (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.

like image 512
Igby Largeman Avatar asked Dec 27 '22 18:12

Igby Largeman


1 Answers

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;
    }
  }
}
like image 162
JaredPar Avatar answered Dec 30 '22 11:12

JaredPar