C# 匿名委托变量引用

C# 匿名委托变量引用,c#,lambda,C#,Lambda,在委托语句中引用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

在委托语句中引用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();
}

由于循环在调用PingCompleted之前完成,因此在第一次调用PingCompleted时,i会增加到3。然后它又被调用了两次,因为这是for循环的初始值,i仍然是3,因为for循环已经完成,不再增加i


你明白我为什么总是出界吗?索引i在第一次迭代时从0到1,在第二次迭代时从1到2,在最后3次迭代时从2到3。所有迭代完成后,将调用对PingCompleted的第一次调用,索引已为3。

由于循环在调用PingCompleted之前完成,因此在第一次调用PingCompleted时,i将增加到3。然后它又被调用了两次,因为这是for循环的初始值,i仍然是3,因为for循环已经完成,不再增加i


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

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

int i = 0;
Func<int> f = ()=>i;
i = 3;
Console.WriteLine(f());
试试看。它做了你认为它应该做的事吗

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


有关详细信息,请参阅。

您希望此程序片段做什么

int i = 0;
Func<int> f = ()=>i;
i = 3;
Console.WriteLine(f());
试试看。它做了你认为它应该做的事吗

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

有关详细信息,请参见C中的。在for循环中,只有一个变量i,当每个PingCompleted处理程序读取i的值时,它会获取该单个变量的当前值,而不是连接处理程序时返回的i的值。因此,如果处理程序在for循环完成后执行,那么我将等于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实例,每个实例都保存该处理程序的正确索引。

在C中。在for循环中,只有一个变量i,当每个PingCompleted处理程序读取i的值时,它会获取该单个变量的当前值,而不是连接处理程序时返回的i的值。因此,如果处理程序在for循环完成后执行,那么我将等于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的不同实例,每一个都包含该处理程序的正确索引。

编辑以解释越界错误。可能循环在调用第一个PingCompleted之前完成,因为它被称为async。@pManac为什么那么糟糕?我想知道它是如何工作的,因为当委托执行时,没有人处理我超出范围的问题…编辑以解释越界错误。可能循环在调用第一个PingCompleted之前完成,因为它被称为async。@PmanAce,为什么那么糟糕?我想知道它是如何工作的,因为当委托执行时,没有人解决我超出范围的问题…啊,所以我在其他注释中出错-值类型不会被捕获为副本,它们作为ref类型被装箱/传递到闭包中?@JerKimball严格来说都不是。有关C编译器将如何实现闭包的近似信息,请参见。它没有引用本地语言,也没有按照传统的C语言的意思将其装箱。它将该变量封装为引用类型的实例字段。干杯,@Servy-我以前看过反射闭包,但它从未真正跳出来。谢谢@JerKimball:在CLR类型系统中,不可能在字段中存储对变量的引用。变量是否为值类型与它的捕获语义无关;
int变量的捕获方式与字符串变量的捕获方式完全相同。我现在跟进,谢谢Eric-我不确定原因,但出于某种原因,我认为生成的代码更符合以下内容:哦,我知道我们需要这个捕获的变量;多亏了编译器,在这个闭包类中有一个空间,我将只使用closure.Capture=liveVariable,在这种情况下,Value vs Ref不起作用。正如我对@Servy所说的,我以前读过反射闭包,但由于某种原因,真实的结构从未注册。啊,所以我在我的其他注释中错了-值类型不会被捕获为副本,它们会作为ref类型装箱/传递到闭包中?@JerKimball,严格来说,两者都不是。有关C编译器将如何实现闭包的近似信息,请参见。它没有引用本地语言,也没有按照传统的C语言的意思将其装箱。它将该变量封装为引用类型的实例字段。干杯,@Servy-我以前看过反射闭包,但它从未真正跳出来。谢谢@JerKimball:在CLR类型系统中,不可能在字段中存储对变量的引用。变量是否为值类型与它的捕获语义无关;int变量的捕获方式与字符串变量的捕获方式完全相同。我现在跟进,谢谢Eric-我不确定原因,但出于某种原因,我认为生成的代码更符合以下内容:哦,我知道我们需要这个捕获的变量;多亏了编译器,在这个闭包类中有一个空间,我将只使用closure.Capture=liveVariable,在这种情况下,Value vs Ref不起作用。正如我对@Servy所说的,我以前读过反射闭包,但由于某种原因,真正的结构从未注册过。