匿名委托变量引用

时间:2013-03-23 00:43:15

标签: c# lambda

在委托声明中引用i是否有效?

targets[i].PingReply = e.Reply;

它是否会引用

中定义的相同数组元素
pingSender.SendAsync( targets[i].IPAddress, targets[i].Timeout);

或者当委托人解雇时,我的价值是不同的?我问,因为我在PingCompleted中得到的索引越界,i = 3,我不知道为什么。

public void Ping(PingTest[] targets)
{
    var finished = new CountdownEvent(targets.Count());
    for (int i = 0; i < targets.Count(); i++)
    {
        finished.AddCount();
        var pingSender = new Ping();
        pingSender.PingCompleted += (sender, e) =>
                                        {
                                            targets[i].PingReply = e.Reply;
                                            finished.Signal();
                                        };
        pingSender.SendAsync(targets[i].IPAddress, targets[i].Timeout);
    }
    finished.Signal();
    finished.Wait();
}

这是电话......

var pingTests = new PingTest[]
                    {
                        new PingTest("Router", new IPAddress(new byte[] {192, 168, 1, 8}), 2),
                        new PingTest("Exchange", new IPAddress(new byte[] {192, 168, 1, 78}), 3),
                        new PingTest("SQL", new IPAddress(new byte[] {192, 168, 1, 99}), 3)
                    };
netwrkService.Ping(pingTests);

3 个答案:

答案 0 :(得分:5)

您希望这个程序片段做什么?

int i = 0;
Func<int> f = ()=>i;
i = 3;
Console.WriteLine(f());

试试吧。它做了你认为应该做的事吗?


匿名函数关闭变量,而不是变量过去的值。当您调用lambda时,循环变量不再具有创建委托时所执行的值。

有关详细信息,请参阅http://ericlippert.com/2009/11/12/closing-over-the-loop-variable-considered-harmful-part-one/

答案 1 :(得分:3)

在C#中,closures close over variables, not over values。在for循环中,只有一个变量i,当每个PingCompleted处理程序读取i的值时,它会获得当前该单个变量的值,而不是处理程序连接时返回的i的值。因此,如果处理程序在for循环结束后执行,那么i将等于3 - 而不是您想要的!

要解决此问题,请将i的值复制到循环内声明的另一个变量中,然后更改处理程序以使用该新变量:

for (int i = 0; i < targets.Count(); i++)
{
    ...
    int j = i;
    pingSender.PingCompleted += (sender, e) =>
                                    {
                                        targets[j].PingReply = e.Reply; // <== j, not i
                                        finished.Signal();
                                    };

在循环中声明变量时,每次迭代都会逻辑地创建变量的新实例。因此,PingCompleted处理程序现在引用j的不同实例,每个实例都包含该处理程序的正确索引。

答案 2 :(得分:0)

由于循环在PingCompleted被调用之前完成,因此当第一次调用PingCompleted时,i增加到3。然后它再被调用两次(因为这是你的for循环初始化我仍然在3,因为for循环已经完成并且不再增加i。

你明白为什么我总是出界吗?您的索引i在第一次迭代时从0变为1,在第二次迭代时从1变为2,在最后一次迭代时变为2到3(在结束时设置为3)。完成所有迭代后,首先调用PingCompleted,索引已经为3。