Przed C# 5, trzeba ponownie zadeklarować zmienną wewnątrz foreach - w przeciwnym razie jest on dzielony, a wszystkie koparki użyje ostatni ciąg:
foreach (string list in lists)
{
string tmp = list;
Button btn = new Button();
btn.Click += new EventHandler(delegate { MessageBox.Show(tmp); });
}
Znacznie pamiętać, że od C# 5 i to się zmieniło, i w szczególności w przypadku foreach
, nie musisz już tego robić: kod w pytaniu będzie działał zgodnie z oczekiwaniami.
Do tego nie działa bez tej zmiany pokazują, należy rozważyć następujące kwestie:
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);
}
Run wyżej przed C# 5, i choć każdy przycisk pokazuje inną nazwę, klikając przyciski pokazuje „Wilma” cztery razy.
to dlatego, że specyfikacja języka (ECMA 334 V4, 15.8.4) (przed C# 5) określa:
foreach (V v in x)
embedded-statement
następnie rozszerzony:
{
E e = ((C)(x)).GetEnumerator();
try {
V v;
while (e.MoveNext()) {
v = (V)(T)e.Current;
embedded-statement
}
}
finally {
… // Dispose e
}
}
Zauważ, że zmienna v
(która jest twoją list
) jest zadeklarowana jako poza pętli. Zgodnie z regułami przechwyconych zmiennych wszystkie iteracje na liście będą dzielić przechwycone zmienne.
Od C# 5 jest to zmienione: zmienna iteracji (v
) ma zasięg wewnątrz pętli. Nie mam odniesienie specyfikacji, ale to w zasadzie staje:
{
E e = ((C)(x)).GetEnumerator();
try {
while (e.MoveNext()) {
V v = (V)(T)e.Current;
embedded-statement
}
}
finally {
… // Dispose e
}
}
Re anulowanie subskrypcji; Jeśli aktywnie chcą wypisać anonimowy obsługi, sztuką jest, aby uchwycić obsługi samego:
EventHandler foo = delegate {...code...};
obj.SomeEvent += foo;
...
obj.SomeEvent -= foo;
Podobnie, jeśli chcesz jednorazowe zdarzeń obsługi (takie jak obciążenie itp):
EventHandler bar = null; // necessary for "definite assignment"
bar = delegate {
// ... code
obj.SomeEvent -= bar;
};
obj.SomeEvent += bar;
To jest teraz self-unsubscribing ;-p
Gratulacje, jesteś teraz częścią dokumentacji Resharper. http: //confluence.jetbrains.net/display/ReSharper/Access + to + modified + closure – Kongress
Ten problem był trudny, ale powyższe wyjaśnienie wyjaśniło mi wyraźnie: _To może wydawać się poprawne, ale w rzeczywistości tylko ostatnia wartość zmiennej str będzie używane po każdym kliknięciu dowolnego przycisku. Powodem tego jest to, że foreach rozwija się do pętli while, ale zmienna iteracyjna jest zdefiniowana poza tą pętlą. Oznacza to, że do czasu wyświetlenia okna komunikatu wartość str mogła już być iterowana do ostatniej wartości w kolekcji ciągów._ – DanielV