Могу ли я иметь метод, возвращающий IEnumerator <T>, и использовать его в цикле foreach? - PullRequest
17 голосов
/ 06 августа 2008

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

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 ]

13 голосов
/ 06 августа 2008

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

10 голосов
/ 09 августа 2008

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

private static IEnumerator<TextBox> FindTextBoxes(Control rootControl)

Меняется на

private static IEnumerable<TextBox> FindTextBoxes(Control rootControl)

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

3 голосов
/ 06 августа 2008

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

1 голос
/ 06 августа 2008
// 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;
    }
}
0 голосов
/ 14 июля 2011

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

...