Puis-je avoir une méthode renvoyant IEnumerator<T> et l'utiliser dans une boucle foreach ?
-
08-06-2019 - |
Question
Je dois définir la hauteur de chaque zone de texte de mon formulaire, dont certaines sont imbriquées dans d'autres contrôles.Je pensais pouvoir faire quelque chose comme ceci :
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;
}
}
}
En l'utilisant comme ceci :
foreach(TextBox textBox in FindTextBoxes(this))
{
textBox.Height = height;
}
Mais bien sûr, le compilateur crache son mannequin, car pour chaque s'attend à un IEnumerable plutôt qu'un IÉnumérateur.
Existe-t-il un moyen de le faire sans avoir à créer une classe distincte avec un ObtenirEnumérateur() méthode?
La solution
Comme le compilateur vous le dit, vous devez changer votre type de retour en IEnumerable.C’est ainsi que fonctionne la syntaxe de rendement et de retour.
Autres conseils
Juste pour clarifier
private static IEnumerator<TextBox> FindTextBoxes(Control rootControl)
Modifications à
private static IEnumerable<TextBox> FindTextBoxes(Control rootControl)
Ce devrait être tout :-)
Si vous renvoyez IEnumerator, ce sera un objet énumérateur différent à chaque appel de cette méthode (agissant comme si vous réinitialisiez l'énumérateur à chaque itération).Si vous retournez IEnumerable, un foreach peut énumérer en fonction de la méthode avec l'instruction rendement.
// 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;
}
}
Si vous recevez un énumérateur et que vous devez l'utiliser dans une boucle for-each, vous pouvez utiliser ce qui suit pour l'envelopper :
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); } }
Le toEnumerable
la méthode acceptera tout ce qui c# ou vb considérerait un type de retour acceptable de GetEnumerator
, et renvoie quelque chose qui peut être utilisé dans foreach
.Si le paramètre est un IEnumerator<>
la réponse sera un IEnumerable<T>
, même si j'appelle GetEnumerator
dessus une fois donnera probablement de mauvais résultats.