Pourquoi C# réutilise la variable dans foreach ? Explication pour débutants

Formateur Python en ligne pour débutants

Apprenez Python facilement sans surcharge théorique. Résolvez des tâches pratiques avec vérification automatique et écrivez du code directement dans le navigateur.

Start Course

Introduction : une énigme qui perturbe les débutants

\n\n

Imaginez la situation : vous écrivez une simple boucle foreach en C#, vous stockez une expression lambda ou lancez une tâche asynchrone à l'intérieur de la boucle, et soudain, vous obtenez un résultat totalement inattendu. Toutes les variables que vous pensiez avoir soigneusement "capturées" pointent en réalité vers le dernier élément de la collection. Cela vous dit quelque chose ?

\n\n

Sur Stack Overflow, la question "Is there a reason for C#'s reuse of the variable in a foreach?" a recueilli des milliers de vues et de nombreux commentaires. Ce n'est pas qu'un simple intérêt académique — c'est un piège pratique dans lequel même les développeurs expérimentés tombent. Dans cet article, nous allons comprendre pourquoi C# agit ainsi, comment cela a évolué avec les versions du langage et, surtout, comment éviter les erreurs qui y sont liées.

\n\n

Nous parlerons simplement, avec des exemples de code, pour que même un débutant en C# saisisse l'essence du problème. C'est parti !

\n\n

Le cœur du problème : que signifie "réutilisation de la variable" ?

\n\n

Dans la boucle foreach classique en C# (avant la version 5.0), la variable d'itération était déclarée une seule fois pour toute la boucle, et non recréée à chaque itération. Cela signifie que si vous capturez cette variable dans une fermeture (par exemple, dans une expression lambda ou une méthode anonyme), toutes les fermetures feront référence à la même variable.

\n\n

Regardons un exemple classique du problème :

\n\n
var actions = new List<Action>();\nvar numbers = new List<int> { 1, 2, 3, 4, 5 };\n\nforeach (var number in numbers)\n{\n    actions.Add(() => Console.WriteLine(number));\n}\n\n// Exécutons toutes les actions\nforeach (var action in actions)\n{\n    action();\n}\n
\n\n

Question : que va afficher ce code ? Intuitivement, on s'attend à 1, 2, 3, 4, 5. Mais en réalité (avant C# 5), il afficherait 5, 5, 5, 5, 5.

\n\n

Pourquoi ? Parce que la variable number existe en un seul exemplaire tout au long de la boucle. Lorsque la boucle se termine, sa valeur est égale au dernier élément (5). Toutes les lambdas ont capturé une référence à cette variable, et non sa valeur au moment de la création.

\n\n

Une analogie pour comprendre

\n\n

Imaginez que vous avez une boîte (la variable) dans laquelle vous placez tour à tour différents objets (les valeurs). Vous donnez à vos amis (les lambdas) non pas les objets eux-mêmes, mais une clé de la boîte. Lorsque vos amis viennent récupérer leurs objets, ils ouvrent la boîte et y trouvent le dernier objet que vous y avez placé. Voilà comment fonctionnait l'ancien foreach.

\n\n

Contexte historique : pourquoi cela a-t-il été fait ainsi ?

\n\n

Beaucoup de débutants demandent : "C'est un bug ! Pourquoi faire ça ?" En réalité, il s'agissait d'une décision délibérée des développeurs de C#, basée sur la performance et la simplicité de compilation.

\n\n
    \n
  • Performance : Créer une nouvelle variable à chaque itération nécessite une allocation mémoire supplémentaire. Pour la plupart des boucles simples, ce n'est pas critique, mais dans les systèmes à forte charge, chaque micro-optimisation compte.
  • \n
  • Cohérence avec for : Dans la boucle for, la variable est également déclarée une seule fois. Les développeurs voulaient maintenir une certaine uniformité.
  • \n
  • Simplicité du compilateur : L'ancienne approche était plus simple à implémenter dans le compilateur. Mais le prix à payer était un comportement contre-intuitif pour les programmeurs.
  • \n
\n\n

Cependant, la communauté a rapidement découvert cette "fonctionnalité" et a commencé à signaler des bugs. Sur Stack Overflow, la question "Is there a reason for C#'s reuse of the variable in a foreach?" est devenue l'une des plus populaires. Microsoft a écouté, et dans C# 5.0 (2012), le comportement a été modifié.

\n\n

Qu'est-ce qui a changé dans C# 5.0 ?

\n\n

À partir de C# 5.0, le compilateur crée une nouvelle variable pour chaque itération de la boucle foreach. Désormais, l'exemple ci-dessus fonctionnera de manière intuitive et affichera 1, 2, 3, 4, 5.

\n\n
// Ceci                

Blogs

Book Recommendations