C#循环初始化带固定索引的Func数组不生效问题求助
问题根因
这是C#闭包的经典问题:for循环里的lambda捕获的是循环变量i本身,不是创建lambda那一刻i的拷贝值。等整个循环执行完成后,i的值固定为10,这时候无论调用数组中哪个委托,读取的都是这同一个i变量,因此所有调用都会等价于执行Convert(10, myVal)。
正确实现方式
写法1:兼容所有C#版本的循环写法
只需要在循环内部声明一个独立的局部变量,将当前轮次的i值拷贝给它即可——每轮循环的局部变量是完全独立的,lambda捕获这个局部变量就不会出现值被覆盖的问题:
var myfunctions= new Func<ushort, double>[10]; for (int i=0; i<10; i++) { int currentIdx = i; // 每轮循环单独存储当前索引 // 注意:原代码lambda内写的`in`是笔误,替换为传入参数x_in myfunctions[i] = x_in => Convert(currentIdx, x_in); }
这个写法的运行效果和你逐行硬编码完全一致,没有冗余代码。
写法2:LINQ简洁写法(C# 3.0及以上可用)
如果项目允许使用LINQ,可以直接一行生成整个函数数组,天然规避闭包捕获问题:
using System.Linq; var myfunctions = Enumerable.Range(0, 10) .Select<int, Func<ushort, double>>(idx => x_in => Convert(idx, x_in)) .ToArray();
这里的idx是Select迭代时传入的形参,每一轮迭代的idx都是独立的值,不存在多个lambda共享同一个变量的问题,代码更简洁也不容易写错。
补充说明:C# 5.0仅调整了
foreach循环的闭包捕获逻辑,for循环的变量捕获逻辑从未变更,即便是最新版本的.NET,直接在for循环的lambda中捕获循环变量i依然会触发这个问题。
内容的提问来源于stack exchange,提问作者Yannick Favre
相关产品推荐
相关产品推荐

