gpt4 book ai didi

c# - "safe"提升 lambda 表达式中引用的局部变量的规则是什么?

转载 作者:行者123 更新时间:2023-12-02 21:41:09 24 4
gpt4 key购买 nike

下面的示例在 throw 语句中引发 IndexOutOfRangeException,因为变量 i 超出了其限制(例如,当循环覆盖 0 和 1 时,为 2)。我期望这段代码创建 lambda block 0 和 1,它们各自将结果存储在相应的数组元素中。我注意到,通过设置断点,异步任务直到我调用 Task.WaitAll() 后才真正开始执行。来自 C# Programming Guide ,我知道编译器在循环退出后不遗余力地将 i 保持在范围内。

所以,我的问题是:

  1. 有人可以建议一种方法来实现我试图创建的效果,即每个异步任务应将其结果存储在数组中的不同槽中吗? Task.Run() 没有重载来提供参数(我用它来在循环中传递 i),并且 lambda block 声明阻止了我的尝试无论如何都要声明参数。

  2. 有人可以提供理由,说明为什么 lambda 表达式在超出其声明 block 的范围后能够继续引用局部变量是理想的行为吗? C# 语言引用扭曲了“本地”声明的真正含义,以涵盖匿名函数和 lambda block 的提升,但这只会打开获取意外值的大门,如我的示例所示。

这是示例:

using System;
using System.Threading.Tasks;

namespace AsyncLifting
{
class Program
{
static void Main(string[] args)
{
const int numTasks = 2;
double[] taskResult = new double[numTasks];
Task<int>[] taskHandles = new Task<int>[numTasks];

for (int i = 0; i < numTasks; i++)
{
taskHandles[i] = Task.Run(async () =>
{
DateTime startTime = DateTime.UtcNow;
await Task.Delay(10);
try
{
taskResult[i] = (DateTime.UtcNow - startTime).TotalMilliseconds;
}
catch (Exception e) {
throw e; // IndexOutOfRange, i is 2
}
return i;
});
}

Task.WaitAll(taskHandles);

Console.WriteLine("Task waits:");
foreach (double tr in taskResult)
{
Console.WriteLine(" {0}ms.", tr);
}
}
}
}

最佳答案

委托(delegate)正在关闭变量——它不仅捕获当时的值,而且捕获整个变量。这有时会很有用。

无论如何,为了防止意外行为,只需创建一个新变量并在 block 内使用它:

for(int i = 0; i < n; i++) {
int index = i;
DoSomething(delegate() {
myArray[index] = /* something */;
});
}

关于c# - "safe"提升 lambda 表达式中引用的局部变量的规则是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20438184/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com