Могу ли я получить метод, возвращающий 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()?

Ответы

Ответ 1

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

Ответ 2

Просто уточнить

private static IEnumerator<TextBox> FindTextBoxes(Control rootControl)

Изменения в

private static IEnumerable<TextBox> FindTextBoxes(Control rootControl)

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

Ответ 3

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

Ответ 4

// Generic function that gets all child controls of a certain type, 
// returned in a List collection
private static List<T> GetChildTextBoxes<T>(Control ctrl) where T : Control{
    List<T> tbs = new List<T>();
    foreach (Control c in ctrl.Controls) {
        // If c is of type T, add it to the collection
        if (c is T) { 
            tbs.Add((T)c);
        }
    }
    return tbs;
}

private static void SetChildTextBoxesHeight(Control ctrl, int height) {
    foreach (TextBox t in GetChildTextBoxes<TextBox>(ctrl)) {
        t.Height = height;
    }
}

Ответ 5

Если вам задан перечислитель и нужно использовать его в цикле 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 примет все, что или будет рассматривать приемлемый тип возврата из GetEnumerator и вернуть что-то, что можно использовать в foreach. Если параметр IEnumerator<>, ответ будет IEnumerable<T>, хотя вызов GetEnumerator на нем один раз даст плохие результаты.