C# 匿名委托变量引用
在委托语句中引用i是否有效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
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所说的,我以前读过反射闭包,但由于某种原因,真正的结构从未注册过。