Comprendre l’itération en C# : Utilisation de IEnumerator<T> dans des boucles foreach

En C#, collaborer avec des collections et itérer à travers des données est une pratique essentielle pour les développeurs. Une question courante se pose lorsque l’on tente de retourner un IEnumerator<T> d’une méthode et de l’utiliser dans une boucle foreach. Cette question devient encore plus intéressante lorsque vous devez parcourir des contrôles imbriqués dans une interface utilisateur.

Plongeons au cœur de cette situation en explorant le problème et en présentant une solution pratique.

Le Problème

Imaginons que vous ayez un Windows Form où vous devez ajuster dynamiquement la hauteur de divers contrôles TextBox. Ces TextBox peuvent non seulement se trouver au niveau racine du formulaire, mais certains peuvent être nichés à l’intérieur d’autres contrôles, créant une structure complexe. Vous souhaitez utiliser une méthode qui emploie yield pour retourner les TextBox trouvés un par un.

Votre approche initiale pourrait ressembler à ceci :

private static IEnumerator<TextBox> FindTextBoxes(Control rootControl)
{
    foreach (Control control in rootControl.Controls)
    {
        if (control.Controls.Count > 0)
        {
            // Recherche récursive de TextBox dans chaque contrôle enfant
            foreach (TextBox textBox in FindTextBoxes(control))
            {
                yield return textBox;
            }
        }

        TextBox textBox2 = control as TextBox;
        if (textBox2 != null)
        {
            yield return textBox2;
        }
    }
}

Cependant, lorsque vous utilisez cette méthode dans une boucle foreach comme ceci :

foreach(TextBox textBox in FindTextBoxes(this))
{
    textBox.Height = height;
}

Vous rencontrez une erreur de compilation. L’erreur se produit parce que la boucle foreach s’attend à un IEnumerable, mais votre méthode retourne un IEnumerator.

La Solution

La clé pour résoudre ce problème réside dans le changement du type de retour de votre méthode de IEnumerator<T> à IEnumerable<T>. La syntaxe yield return en C# permet à la méthode de produire efficacement un itérateur sans avoir besoin d’une classe d’énumérateur séparée.

Voici comment modifier votre méthode :

Méthode Modifiée

private static IEnumerable<TextBox> FindTextBoxes(Control rootControl)
{
    foreach (Control control in rootControl.Controls)
    {
        // Vérifier s'il y a des contrôles enfants
        if (control.Controls.Count > 0)
        {
            // Recherche récursive de TextBox dans les contrôles enfants
            foreach (TextBox textBox in FindTextBoxes(control))
            {
                yield return textBox;
            }
        }

        // Si le contrôle actuel est un TextBox, le retourner
        if (control is TextBox textBox2)
        {
            yield return textBox2;
        }
    }
}

Explication

  1. Changer le Type de Retour : La méthode retourne désormais IEnumerable<TextBox> au lieu de IEnumerator<TextBox>. Cela signifie qu’elle peut désormais être utilisée directement dans une boucle foreach.

  2. Utilisation de yield return : L’utilisation de yield return fournit un moyen propre et efficace de récupérer les contrôles TextBox au fur et à mesure que la boucle progresse.

Avec ce changement, l’utilisation de la méthode dans votre boucle foreach fonctionnera désormais sans problème, et chaque TextBox sera traitée individuellement.

Conclusion

Travailler avec des collections C# et des boucles foreach peut parfois conduire à des pièges inattendus, surtout lorsqu’il s’agit de méthodes récursives et de structures de contrôle. En modifiant le type de retour de IEnumerator<T> à IEnumerable<T>, vous pouvez facilement utiliser l’énumération dans les constructions de boucle sans complexités supplémentaires.

En suivant cette approche, non seulement vous éviterez les erreurs de compilation, mais vous améliorerez également la maintenabilité et la lisibilité de votre code lors de la gestion des contrôles imbriqués sur votre formulaire.

Pour en savoir plus, familiarisez-vous avec des concepts tels que IEnumerable, IEnumerator, et le mot-clé yield afin de tirer pleinement parti des capacités d’itération de C#.