Blazor中for循环索引作为onclick参数失效,为何需重新赋值变量?
Blazor循环绑定事件的坑:为什么要重新赋值局部变量?
你遇到的这个问题和Blazor本身无关,本质是C#的循环变量作用域+闭包捕获机制搞的鬼,Blazor只是因为事件触发是延迟的,把这个问题放大了。
先掰明白循环变量的本质:
- 你写的for循环里,
row和col是在循环外面声明的,整个循环过程中用的是同一个变量实例——每次迭代只是更新它的数值,不是新建一个变量。 - 当你给每个像素绑定
@onclick="() => LogClickedPixel(row, col)"时,这个lambda表达式并没有直接把当时的row、col值存下来,而是捕获了这两个变量的引用。
问题就出在这:循环跑的速度比你点击像素快多了,等你点像素的时候,循环早跑完了,row和col已经变成了最后一次迭代的数值。这时候触发lambda,拿到的自然就是循环最后的那组值。
那为什么赋值给局部变量就好使?
- 当你在循环内部写
var currentRow = row; var currentCol = col;,这个currentRow和currentCol是每次迭代都会新建的独立变量,每个迭代的局部变量都有自己的作用域和数值。 - lambda捕获的是当前迭代的局部变量引用,后续迭代不会修改它的值,所以点击对应像素时,就能拿到正确的row和col了。
举个代码对比更清楚:
错误写法(点击全传最后一组值)
@for (int row = 0; row < GridHeight; row++) { @for (int col = 0; col < GridWidth; col++) { <div @onclick="() => LogClickedPixel(row, col)" class="pixel"></div> } }
正确写法(点击传对应位置值)
@for (int row = 0; row < GridHeight; row++) { @for (int col = 0; col < GridWidth; col++) { var currentRow = row; var currentCol = col; <div @onclick="() => LogClickedPixel(currentRow, currentCol)" class="pixel"></div> } }
补充一句:这个坑不是Blazor独有的,C#里只要用闭包捕获for循环变量的场景都会踩——比如异步任务、定时器回调。另外,C# 5.0之后foreach循环的变量改成了每次迭代新建实例,但for循环至今还是老逻辑,所以得自己手动搞局部变量。
内容的提问来源于stack exchange,提问作者Ondrej Sevcak
相关产品推荐
相关产品推荐

