问题
写C#窗口程序,今天遇到的问题。在工作线程(非UI线程)要操作ListView,因此使用了跨线程调用方式。
for (int i = 0; i < videos.Count; ++i)
{
this.BeginInvoke(new Action(() =>
{
var item = lvwVideos.FindItemWithText(videos[i]);
if (item != null)
lvwVideos.Items.Remove(item);
}));
}
代码一跑给我抛出了异常,数组下标越界。原因分析如下。
分析
C#的闭包环境并不会复制用到的局部变量。
.NET对闭包的实现是在编译阶段而不是运行阶段,事实上,匿名函数中的变量 i 和 for 循环中的i就是同一个变量,由于函数返回后变量还会被匿名函数使用,它会保存在堆中而不是调用栈——不管这个变量是值类型还是引用类型(如果是引用类型即对象和对象的引用都在堆中)。
因此,循环结束后 i 的值已经超出数组 videos 的下标范围。而BeginInvoke函数是不等待执行完毕的,因此很可能循环结束而匿名函数还没有执行完毕。这时匿名函数从堆中取到的 i 已经不是定义匿名函数时 i 的值了。
用下面的代码来说可能更清晰。
static void Main(string[] args)
{
int i