Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/24.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 使用修改后的封口(2)_C#_.net_Resharper_Closures - Fatal编程技术网

C# 使用修改后的封口(2)

C# 使用修改后的封口(2),c#,.net,resharper,closures,C#,.net,Resharper,Closures,这是问题的延伸。我只是想验证一下,对于生产使用而言,以下各项是否确实足够安全 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.S

这是问题的延伸。我只是想验证一下,对于生产使用而言,以下各项是否确实足够安全

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); });
}
列表列表=新列表();
//从数据库中检索列表的代码
foreach(列表中的字符串列表)
{
按钮btn=新按钮();
btn.Click+=neweventhandler(委托{MessageBox.Show(list);});
}
我每次启动时只运行一次上述操作。目前看来,它似乎运作良好。正如乔恩所提到的,在某些情况下会产生反直觉的结果。那么我需要在这里注意什么呢?如果列表多次运行是否可以?

在C#5之前,您需要在foreach中重新声明一个变量-否则它将被共享,并且您的所有处理程序将使用最后一个字符串:

foreach (string list in lists)
{
    string tmp = list;
    Button btn = new Button();
    btn.Click += new EventHandler(delegate { MessageBox.Show(tmp); });
}
值得注意的是,从C#5开始,这种情况已经发生了变化,特别是在
foreach
的情况下,您不需要再这样做了:问题中的代码将按预期工作

显示没有此变化的情况下不工作,请考虑以下内容:

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之前运行上述,尽管每个按钮显示不同的名称,但单击按钮四次会显示“Wilma”

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

foreach(V在x中)
嵌入语句
然后展开为:

{
    E e = ((C)(x)).GetEnumerator();
    try {
        V v;
         while (e.MoveNext()) {
            v = (V)(T)e.Current;
             embedded-statement
        }
    }
    finally {
        … // Dispose e
    }
}
请注意,变量
v
(它是您的
列表
)在循环之外声明。因此,根据捕获变量的规则,列表的所有迭代都将共享捕获的变量持有者

从C#5开始,这一点发生了变化:迭代变量(
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等):


现在是自动退订-p

如果是这种情况,临时变量将保留在内存中,直到应用程序关闭,以便为代理服务,如果变量占用大量内存,则不建议对非常大的循环执行此操作。我说的对吗?事件中有一些东西(按钮)会在记忆中保留一段时间。有一种方法可以取消订阅一次性委托,我将在文章中添加这种方法。但要说明您的观点:是的,捕获的变量确实可以增加变量的范围。你需要小心不要捕捉到你没有预料到的东西……你能更新一下你关于C#5.0规范变化的答案吗?只是为了让它成为关于C#中foreach循环的优秀wiki文档。关于C#5.0编译器处理foreach循环的更改,已经有一些很好的答案,但它们不是类似于wiki的资源。@Kos是的,的
在5.0计算中没有更改,您现在是Resharper文档的一部分。这是一个棘手的问题,但上面的解释让我明白了:这可能看起来是正确的,但实际上,只要单击任何按钮,都只会使用str变量的最后一个值。原因是foreach展开到while循环中,但迭代变量是在该循环之外定义的。这意味着,在显示消息框时,str的值可能已经迭代到strings集合中的最后一个值。
EventHandler bar = null; // necessary for "definite assignment"
bar = delegate {
  // ... code
  obj.SomeEvent -= bar;
};
obj.SomeEvent += bar;