Ответ 1
Как компилятор вам сообщает, вам нужно изменить тип возвращаемого значения на IEnumerable. Вот как работает синтаксис yield yield.
Мне нужно установить высоту каждого текстового поля в моей форме, некоторые из которых вложены в другие элементы управления. Я думал, что смогу сделать что-то вроде этого:
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()?
Как компилятор вам сообщает, вам нужно изменить тип возвращаемого значения на IEnumerable. Вот как работает синтаксис yield yield.
Просто уточнить
private static IEnumerator<TextBox> FindTextBoxes(Control rootControl)
Изменения в
private static IEnumerable<TextBox> FindTextBoxes(Control rootControl)
Это должно быть все: -)
Если вы вернете IEnumerator, это будет другой объект перечислителя каждый раз, когда вы вызовете этот метод (действуя, как если бы вы reset перечислитель на каждой итерации). Если вы возвращаете IEnumerable, то foreach может перечислять на основе метода с помощью инструкции yield.
// 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, вы можете использовать следующее для его переноса:
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
примет все, что С# или vb будет рассматривать приемлемый тип возврата из GetEnumerator
и вернуть что-то, что можно использовать в foreach
. Если параметр IEnumerator<>
, ответ будет IEnumerable<T>
, хотя вызов GetEnumerator
на нем один раз даст плохие результаты.