Capture des variables d’itération via des lambda expressions
Si vous utilisez TPL avec le Framework 4.0, vous avez sans doute noté l’usage des boucles de type for ou foreach, contenant une expression lambda depuis Visual Studio 2010 et C# 4.0, peut donner des résultats inattendus lorsque vous la variable d’itération est utilisée.
Boucle for contenant une expression Lambda
Dans le cadre de TPL, vous pourriez écrire du code avec cette expression lambda.
for (int i = 0; i < 5; i++)
{
Task.Factory.StartNew(() => Console.WriteLine(i));
}
Console.ReadKey();
Si vous exécutez ce code, vous obtiendrez ce genre de résultats :
Lorsque vous capturez la variable d’itération dans une boucle, C# considère cette variable comme déclarée à l’extérieur de la boucle. Ceci signifie que la même variable est capturée à chaque itération. Pour corriger ce problème, il vous faut introduire une variable temporaire au sein de la boucle.
for (int i = 0; i < 5; i++)
{
int i1 = i;
Task.Factory.StartNew(() => Console.WriteLine(i1));
}
Console.ReadKey();
Ce comportement existe à la fois en C# 4.0 et en C# 5.0 vis-à-vis des boucles for.
Boucle foreach contenant une expression Lambda
Passons à utilisation une boucle foreach.
var list = Enumerable.Range(0, 5);
foreach (var item in list)
{
Task.Factory.StartNew(() => Console.WriteLine(item));
}
Console.ReadKey();
Pour corriger ce code en C# 4.0 vous devez appliquer la même technique : introduire une variable temporaire au sein de la boucle.
var list = Enumerable.Range(0, 5);
foreach (var item in list)
{
var i = item;
Task.Factory.StartNew(() => Console.WriteLine(i));
}
Console.ReadKey();
Si vous passez en C# 5.0, vous n’avez plus à modifier votre code, votre boucle foreach fonctionnera correctement sans ajout d’une variable temporaire.
var list = Enumerable.Range(0, 5);
foreach (var item in list)
{
Task.Factory.StartNew(() => Console.WriteLine(item));
}
Console.ReadKey();
En résumé
Si vous travaillez en C# 4.0 avec Visual Studio 2010, vous devez systématiquement vérifier si vos variables d’itérations sont utilisées au sein d’une expression lambda. Si vous utilisez Visual Studio 2012 avec C# 5.0, vous devriez aussi vérifier vos itérations excepté les boucles foreach.
A bientôt
Bruno