Posso ter um método retornando IEnumerator<T> e usá-lo em um loop foreach?
-
08-06-2019 - |
Pergunta
Preciso definir a altura de cada caixa de texto no meu formulário, algumas das quais estão aninhadas em outros controles.Eu pensei que poderia fazer algo assim:
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;
}
}
}
Usando assim:
foreach(TextBox textBox in FindTextBoxes(this))
{
textBox.Height = height;
}
Mas é claro que o compilador cospe seu boneco, porque para cada espera um IEnumerable em vez de um IEnumerador.
Existe uma maneira de fazer isso sem ter que criar uma classe separada com um GetEnumerator() método?
Solução
Como o compilador está informando, você precisa alterar seu tipo de retorno para IEnumerable.É assim que funciona a sintaxe de retorno de rendimento.
Outras dicas
Só para esclarecer
private static IEnumerator<TextBox> FindTextBoxes(Control rootControl)
Muda para
private static IEnumerable<TextBox> FindTextBoxes(Control rootControl)
Isso deve ser tudo :-)
Se você retornar IEnumerator, será um objeto enumerador diferente cada vez que chamar esse método (agindo como se você redefinisse o enumerador em cada iteração).Se você retornar IEnumerable, um foreach poderá enumerar com base no método com a instrução de rendimento.
// 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;
}
}
Se você receber um enumerador e precisar usá-lo em um loop for-each, poderá usar o seguinte para envolvê-lo:
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); } }
O toEnumerable
método aceitará qualquer coisa que c# ou vb consideraria um tipo de retorno aceitável de GetEnumerator
, e retorne algo que possa ser usado em foreach
.Se o parâmetro for um IEnumerator<>
a resposta será uma IEnumerable<T>
, embora ligando GetEnumerator
fazer isso uma vez provavelmente produzirá resultados ruins.