22

I need to set the height of every textbox on my form, some of which are nested within other controls. I thought I could do something like this:

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

Using it like this:

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

But of course the compiler spits its dummy, because foreach expects an IEnumerable rather than an IEnumerator.

Is there a way to do this without having to create a separate class with a GetEnumerator() method?

Vitor
  • 523
  • 2
  • 4
  • 28
littlecharva
  • 263
  • 1
  • 4
  • 5
  • 2
    Actually, `foreach` doesn’t expect an `IEnumerable` at all (nor an `IEnumerable`). It only expects something that has a `GetEnumerator` method. That method, in turn, need not necessarily return an `IEnumerator` or `IEnumerator` — it only needs to return something that has a `MoveNext()` method and a `Current` property. – Timwi Sep 01 '10 at 14:22

5 Answers5

14

As the compiler is telling you, you need to change your return type to IEnumerable. That is how the yield return syntax works.

David Wengier
  • 10,061
  • 5
  • 39
  • 43
  • 7
    `yield return` can be used with methods that return *either* `IEnumerable` *or* `IEnumerator`. It’s only in the `foreach` loop where `IEnumerator` can’t be used. – Timwi Sep 01 '10 at 14:20
10

Just to clarify

private static IEnumerator<TextBox> FindTextBoxes(Control rootControl)

Changes to

private static IEnumerable<TextBox> FindTextBoxes(Control rootControl)

That should be all :-)

Orion Edwards
  • 121,657
  • 64
  • 239
  • 328
3

If you return IEnumerator, it will be a different enumerator object each time call that method (acting as though you reset the enumerator on each iteration). If you return IEnumerable then a foreach can enumerate based on the method with the yield statement.

Joseph Daigle
  • 47,650
  • 10
  • 49
  • 73
1
// 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;
    }
}
Abhishek
  • 2,925
  • 4
  • 34
  • 59
Yaakov Ellis
  • 40,752
  • 27
  • 129
  • 174
0

If you are given an enumerator, and need to use it in a for-each loop, you could use the following to wrap it:

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

The toEnumerable method will accept anything that or would regard an acceptable return type from GetEnumerator, and return something that can be used in foreach. If the parameter is an IEnumerator<> the response will be an IEnumerable<T>, though calling GetEnumerator on it once will likely yield bad results.

Abhishek
  • 2,925
  • 4
  • 34
  • 59
supercat
  • 77,689
  • 9
  • 166
  • 211