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

Ответов (5)

Решение

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

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

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

private static IEnumerator<TextBox> FindTextBoxes(Control rootControl)

Изменения к

private static IEnumerable<TextBox> FindTextBoxes(Control rootControl)

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

// 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;
    }
}

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

статический открытый класс enumerationHelper
{
    общедоступный класс enumeratorHolder <T>
    {
        частный T theEnumerator;
        общедоступный T GetEnumerator () {вернуть TheEnumerator; }
        public enumeratorHolder (T newEnumerator) {theEnumerator = newEnumerator;}
    }
    статический enumeratorHolder <T> toEnumerable <T> (T theEnumerator) {вернуть новый enumeratorHolder <T> (theEnumerator); }
    частный класс IEnumeratorHolder <T>: IEnumerable <T>
    {
        частный IEnumerator <T> theEnumerator;
        общедоступный IEnumerator <T> GetEnumerator () {возвратите theEnumerator; }
        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator () {вернуть theEnumerator; }
        общедоступный IEnumeratorHolder (IEnumerator <T> newEnumerator) {theEnumerator = newEnumerator; }
    }
    статический IEnumerable <T> toEnumerable <T> (IEnumerator <T> theEnumerator) {вернуть новый IEnumeratorHolder <T> (theEnumerator); }
}

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