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