访问修改的closures(2)

这是从Access到修改closures的问题的扩展。 我只是想validation以下是否足够安全的生产使用。

List<string> lists = new List<string>(); //Code to retrieve lists from DB foreach (string list in lists) { Button btn = new Button(); btn.Click += new EventHandler(delegate { MessageBox.Show(list); }); } 

我每次启动时只运行一次。 现在看来工作好了。 正如Jon在某些情况下所提到的违反直觉的结果。 那么我需要在这里注意什么? 如果列表多次运行,会不会好?

在C#5之前,您需要在foreach中重新声明一个variables – 否则它将被共享,并且您的所有处理程序都将使用最后一个string:

 foreach (string list in lists) { string tmp = list; Button btn = new Button(); btn.Click += new EventHandler(delegate { MessageBox.Show(tmp); }); } 

值得注意的是,从C#5开始,这已经发生了变化, 特别是在foreach的情况下 ,您不需要再这样做:问题中的代码将按预期工作。

为了certificate这一点,如果没有这个改变就不能正常工作了

 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); } 

在C#5之前运行以上,尽pipe每个button显示不同的名称,单击button会显示“Wilma”四次。

这是因为语言规范(ECMA 334 v4,15.8.4)(在C#5之前)定义:

foreach (V v in x) embedded-statement展开为:

 { E e = ((C)(x)).GetEnumerator(); try { V v; while (e.MoveNext()) { v = (V)(T)e.Current; embedded-statement } } finally { … // Dispose e } } 

请注意,variablesv (这是您的list )在循环之外声明。 所以通过捕获variables的规则,列表的所有迭代将共享捕获的variables的持有者。

从C#5开始,这被改变了:迭代variables( v )被定义循环内部 。 我没有规范参考,但基本上变成:

 { E e = ((C)(x)).GetEnumerator(); try { while (e.MoveNext()) { V v = (V)(T)e.Current; embedded-statement } } finally { … // Dispose e } } 

重新取消订阅; 如果你主动想取消订阅一个匿名处理程序,诀窍是捕获处理程序本身:

 EventHandler foo = delegate {...code...}; obj.SomeEvent += foo; ... obj.SomeEvent -= foo; 

同样,如果你想要一个只有一个的事件处理程序(如Load等):

 EventHandler bar = null; // necessary for "definite assignment" bar = delegate { // ... code obj.SomeEvent -= bar; }; obj.SomeEvent += bar; 

这是现在自我取消订阅; – P