Почему мой IEnumerable UserControl удаляется после того, как я перечислил его с помощью foreach? - PullRequest
4 голосов
/ 21 июня 2011

У меня есть 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());
    }
}

Запустите его в отладчике и щелкните черный квадрат.

Ответы [ 3 ]

4 голосов
/ 21 июня 2011

Один из интерфейсов, реализованных IEnumerator, - IDisposable.Цикл foreach вызовет Dispose в источнике цикла, как только он завершит обработку элементов.

Гораздо лучшим решением было бы разделить ваш UserControl на 2 части

  1. UserControl минус перечислимые интерфейсы
  2. Свойство / метод, который выставляет коллекцию, которую он содержит

Например

public class VanishingControl : UserControl
{
  string[] strings = { "uno", "due", "tres" };

  public IEnumerable<string> GetItems() {
    foreach (var current in strings) {
      yield return current;
    }
  }
}
4 голосов
/ 21 июня 2011

Поскольку ваш элемент управления является собственным перечислителем: -)

Перечислитель располагается после foreach автоматически.

Я бы предложил сделать перечислитель отдельным классом.Или, в качестве альтернативы, просто используйте стандартную коллекцию в качестве члена и не устанавливайте элемент управления как IEnumerable.

2 голосов
/ 21 июня 2011

Это причина:

public IEnumerator GetEnumerator()
{
    return this;
}

Одним из интерфейсов IEnumerator является IDisposable, цикл foreach в конце работы вызывает метод Dispose.

Вы должны предоставлять новый IEnumerator каждый раз, когда вызывается метод GetEnumerator, и избегать использования обоих интерфейсов в одной реализации. Это сделано LINQ, но это конкретная ситуация.

Вместо IEnumerator в классе VanishingControl у вас должен быть подкласс, который реализует IEnumerator, и в GetEnumeratiorn возвращается новое значение этого класса.

...