Trước C # 5, bạn cần khai báo lại một biến bên trong foreach - nếu không, nó sẽ được chia sẻ và tất cả các trình xử lý của bạn sẽ sử dụng chuỗi cuối cùng:
foreach (string list in lists)
{
string tmp = list;
Button btn = new Button();
btn.Click += new EventHandler(delegate { MessageBox.Show(tmp); });
}
Đáng chú ý, hãy lưu ý rằng từ C # 5 trở đi, điều này đã thay đổi, và cụ thể trong trường hợpforeach
, bạn không cần phải làm điều này nữa: mã trong câu hỏi sẽ hoạt động như mong đợi.
Để hiển thị điều này không hoạt động nếu không có thay đổi này, hãy xem xét những điều sau:
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);
}
Chạy phần trên trước C # 5 và mặc dù mỗi nút hiển thị một tên khác nhau, việc nhấp vào các nút sẽ hiển thị "Wilma" bốn lần.
Điều này là do thông số ngôn ngữ (ECMA 334 v4, 15.8.4) (trước C # 5) xác định:
foreach (V v in x)
embedded-statement
sau đó được mở rộng thành:
{
E e = ((C)(x)).GetEnumerator();
try {
V v;
while (e.MoveNext()) {
v = (V)(T)e.Current;
embedded-statement
}
}
finally {
… // Dispose e
}
}
Lưu ý rằng biến v
(là của bạn list
) được khai báo bên ngoài vòng lặp. Vì vậy, theo quy tắc của các biến được bắt, tất cả các lần lặp lại của danh sách sẽ chia sẻ người giữ biến được bắt.
Từ C # 5 trở đi, điều này được thay đổi: biến lặp ( v
) nằm trong phạm vi vòng lặp. Tôi không có tham chiếu đặc tả, nhưng về cơ bản nó trở thành:
{
E e = ((C)(x)).GetEnumerator();
try {
while (e.MoveNext()) {
V v = (V)(T)e.Current;
embedded-statement
}
}
finally {
… // Dispose e
}
}
Hủy đăng ký lại; nếu bạn chủ động muốn hủy đăng ký một trình xử lý ẩn danh, mẹo là nắm bắt chính trình xử lý:
EventHandler foo = delegate {...code...};
obj.SomeEvent += foo;
...
obj.SomeEvent -= foo;
Tương tự như vậy, nếu bạn muốn một trình xử lý sự kiện chỉ dùng một lần (chẳng hạn như Tải, v.v.):
EventHandler bar = null; // necessary for "definite assignment"
bar = delegate {
// ... code
obj.SomeEvent -= bar;
};
obj.SomeEvent += bar;
Điều này hiện đang tự hủy đăng ký ;-p