Могу ли я иметь метод, возвращающий IEnumerator‹T›, и использовать его в цикле foreach?

Мне нужно установить высоту каждого текстового поля в моей форме, некоторые из которых вложены в другие элементы управления. Я думал, что могу сделать что-то вроде этого:

private static IEnumerator<TextBox> FindTextBoxes(Control rootControl)
{
    foreach (Control control in rootControl.Controls)
    {
        if (control.Controls.Count > 0)
        {
            // Recursively search for any TextBoxes within each child control
            foreach (TextBox textBox in FindTextBoxes(control))
            {
                yield return textBox;
            }
        }

        TextBox textBox2 = control as TextBox;
        if (textBox2 != null)
        {
            yield return textBox2;
        }
    }
}

Используя его следующим образом:

foreach(TextBox textBox in FindTextBoxes(this))
{
    textBox.Height = height;
}

Но, конечно же, компилятор плюет на свою пустышку, потому что foreach ожидает IEnumerable, а не IEnumerator.

Есть ли способ сделать это без создания отдельного класса с методом GetEnumerator()?


person littlecharva    schedule 06.08.2008    source источник
comment
На самом деле, foreach вообще не ожидает IEnumerableIEnumerable<T>). Он ожидает только то, что имеет метод GetEnumerator. Этот метод, в свою очередь, не обязательно должен возвращать IEnumerator или IEnumerator<T> — ему нужно только вернуть то, что имеет метод MoveNext() и свойство Current.   -  person Timwi    schedule 01.09.2010


Ответы (4)


Как говорит вам компилятор, вам нужно изменить тип возвращаемого значения на IEnumerable. Вот как работает синтаксис yield return.

person David Wengier    schedule 06.08.2008
comment
yield return можно использовать с методами, которые возвращают либо IEnumerable<T> или IEnumerator<T>. Только в цикле foreach нельзя использовать IEnumerator<T>. - person Timwi; 01.09.2010

Просто для уточнения

private static IEnumerator<TextBox> FindTextBoxes(Control rootControl)

Изменения к

private static IEnumerable<TextBox> FindTextBoxes(Control rootControl)

Это все должно быть :-)

person Orion Edwards    schedule 08.08.2008

Если вы возвращаете IEnumerator, это будет другой объект перечислителя при каждом вызове этого метода (действуя так, как будто вы сбрасываете перечислитель на каждой итерации). Если вы возвращаете IEnumerable, то foreach может перечислить на основе метода с оператором yield.

person Joseph Daigle    schedule 06.08.2008

Если вам дан перечислитель и вам нужно использовать его в цикле for-each, вы можете использовать следующее, чтобы обернуть его:

static public class enumerationHelper
{
    public class enumeratorHolder<T>
    {
        private T theEnumerator;
        public T GetEnumerator() { return theEnumerator; }
        public enumeratorHolder(T newEnumerator) { theEnumerator = newEnumerator;}
    }
    static enumeratorHolder<T> toEnumerable<T>(T theEnumerator) { return new enumeratorHolder<T>(theEnumerator); }
    private class IEnumeratorHolder<T>:IEnumerable<T>
    {
        private IEnumerator<T> theEnumerator;
        public IEnumerator<T> GetEnumerator() { return theEnumerator; }
        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return theEnumerator; }
        public IEnumeratorHolder(IEnumerator<T> newEnumerator) { theEnumerator = newEnumerator; }
    }
    static IEnumerable<T> toEnumerable<T>(IEnumerator<T> theEnumerator) { return new IEnumeratorHolder<T>(theEnumerator); }
}

Метод toEnumerable примет все, что c# или vb будет рассматривать допустимый тип возвращаемого значения из GetEnumerator, и вернуть то, что можно использовать в foreach. Если параметр равен IEnumerator<>, ответом будет IEnumerable<T>, хотя однократный вызов GetEnumerator, скорее всего, приведет к плохим результатам.

person supercat    schedule 14.07.2011