访问修改的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