Posso avere un metodo che restituisce IEnumerator<T> e utilizzarlo in un ciclo foreach?
-
08-06-2019 - |
Domanda
Devo impostare l'altezza di ogni casella di testo sul mio modulo, alcune delle quali sono nidificate all'interno di altri controlli.Pensavo di poter fare qualcosa del genere:
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;
}
}
}
Usandolo in questo modo:
foreach(TextBox textBox in FindTextBoxes(this))
{
textBox.Height = height;
}
Ma ovviamente il compilatore sputa il suo manichino, perché per ciascuno si aspetta un IEnumerabile piuttosto che un IEnumerator.
C'è un modo per farlo senza dover creare una classe separata con a GetEnumerator() metodo?
Soluzione
Come ti dice il compilatore, devi cambiare il tipo restituito in IEnumerable.Ecco come funziona la sintassi yield return.
Altri suggerimenti
Giusto per chiarire
private static IEnumerator<TextBox> FindTextBoxes(Control rootControl)
Cambia in
private static IEnumerable<TextBox> FindTextBoxes(Control rootControl)
Dovrebbe essere tutto :-)
Se restituisci IEnumerator, sarà un oggetto enumeratore diverso ogni volta che chiami quel metodo (agendo come se reimpostassi l'enumeratore a ogni iterazione).Se restituisci IEnumerable, un foreach può enumerare in base al metodo con l'istruzione 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;
}
}
Se ti viene fornito un enumeratore e devi utilizzarlo in un ciclo for-each, puoi utilizzare quanto segue per racchiuderlo:
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); } }
IL toEnumerable
il metodo accetterà tutto ciò C# O vb considererebbe un tipo di reso accettabile da GetEnumerator
, e restituisce qualcosa che può essere utilizzato in foreach
.Se il parametro è un IEnumerator<>
la risposta sarà un IEnumerable<T>
, anche se chiama GetEnumerator
su di esso una volta probabilmente si otterranno risultati negativi.