Avant C# 5, vous avez besoin de re-déclarer une variable à l'intérieur de la boucle foreach - sinon, il est partagé, et tous vos gestionnaires d'utiliser la dernière chaîne:
foreach (string list in lists)
{
string tmp = list;
Button btn = new Button();
btn.Click += new EventHandler(delegate { MessageBox.Show(tmp); });
}
De manière significative, note qu'à partir de C# à partir de 5, cela a changé, et spécifiquement dans le cas de l' foreach
, vous n'avez pas besoin de faire cela une fois de plus: le code en question ne fonctionnent pas comme prévu.
Pour montrer cela ne fonctionne pas sans ce changement, considérez les points suivants:
string[] names = { "Fred", "Barney", "Betty", "Wilma" };
using (Form form = new Form())
{
foreach (string name in names)
{
Button btn = new Button();
btn.Text = name;
btn.Click += delegate
{
MessageBox.Show(form, name);
};
btn.Dock = DockStyle.Top;
form.Controls.Add(btn);
}
Application.Run(form);
}
Exécuter le dessus avant C# 5, et bien que chaque bouton indique un nom différent, en cliquant sur les boutons de la montre "Wilma" quatre fois.
C'est parce que la langue spec (ECMA 334 v4, 15.8.4) (avant C# 5) définit:
foreach (V v in x)
embedded-statement
est ensuite étendu à:
{
E e = ((C)(x)).GetEnumerator();
try {
V v;
while (e.MoveNext()) {
v = (V)(T)e.Current;
embedded-statement
}
}
finally {
… // Dispose e
}
}
Notez que la variable v
(ce qui est ton list
) est déclarée en dehors de la boucle. Par les règles de la capture de variables, toutes les itérations de la liste de partager la capture de variable titulaire.
À partir de C# à partir de 5, c'est changé: la variable d'itération (v
) est étendue à l'intérieur de la boucle. Je n'ai pas de spécification de référence, mais fondamentalement, ça devient:
{
E e = ((C)(x)).GetEnumerator();
try {
while (e.MoveNext()) {
V v = (V)(T)e.Current;
embedded-statement
}
}
finally {
… // Dispose e
}
}
Re désabonnement; si vous souhaitez vous désabonner d'un anonyme du gestionnaire, l'astuce est de capturer le gestionnaire lui-même:
EventHandler foo = delegate {...code...};
obj.SomeEvent += foo;
...
obj.SomeEvent -= foo;
De même, si vous voulez un seul gestionnaire d'événements (comme la Charge, etc):
EventHandler bar = null; // necessary for "definite assignment"
bar = delegate {
// ... code
obj.SomeEvent -= bar;
};
obj.SomeEvent += bar;
C'est maintenant l'auto-désabonnement ;-p