1

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

targets[i].PingReply = e.Reply;

它会引用定义在

pingSender.SendAsync( targets[i].IPAddress, targets[i].Timeout);

还是当委托触发时 i 的值会有所不同?我问是因为我在 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);
4

3 回答 3

5

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

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

尝试一下。它做了你认为它应该做的事吗?


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

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

于 2013-03-23T02:45:50.030 回答
3

在 C# 中,闭包关闭变量,而不是值。在您的for循环中,只有一个 variable i,并且当每个PingCompleted处理程序读取 的值时i,它会获取该单个变量的当前值,而不是i处理程序连接时的 back 值。因此,如果处理程序在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,每个实例都包含该处理程序的正确索引。

于 2013-03-23T02:46:21.150 回答
0

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

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

于 2013-03-23T01:55:22.123 回答