C#中Lambda表达式可访问myRect,传统方法却无法访问的原因咨询
myRect,而传统事件处理方法不行? 这事儿核心是**C#闭包(Closure)**的特性,以及局部变量作用域的限制,咱们掰开揉碎了说:
1. Lambda的闭包帮你"抓住"了myRect
当你写SB.Completed += (s,e) => myCanvas.Children.Remove(myRect);的时候,这个Lambda表达式形成了一个闭包——它会把当前myCanvas_MouseMove方法里的局部变量myRect的引用"捕获"下来,哪怕myCanvas_MouseMove方法执行完退出了,这个引用也会被保留,直到Completed事件触发。
换句话说:每次鼠标移动生成新的myRect和对应的Storyboard时,这个Lambda就绑定到了当前这一个特定的myRect实例上,等动画跑完,它能精准找到要移除的那个矩形,不会搞混。
2. 传统事件方法看不到局部变量myRect
你的SB_Completed是类级别的方法,它不在myCanvas_MouseMove的局部作用域里——局部变量的作用域只限于定义它的方法内部,类的其他方法根本没权限直接访问这个变量。
你现在用myCanvas.Children.RemoveAt(0)其实是凑合用,但风险很大:如果同时有多个动画在运行,你移除的大概率不是刚完成动画的那个矩形,很容易出bug。
那传统方法怎么精准拿到对应的myRect?
不想用Lambda也没关系,你可以通过Storyboard的Tag属性把目标矩形存起来:
// 在绑定事件前,把myRect存到Storyboard的Tag里 SB.Tag = myRect; SB.Completed += new EventHandler(SB_Completed);
然后在SB_Completed里取出来使用:
void SB_Completed(object sender, EventArgs e) { Storyboard finishedSB = sender as Storyboard; // 先做安全校验,避免空引用 if (finishedSB?.Tag is Rectangle targetRect) { myCanvas.Children.Remove(targetRect); } }
这样就能和Lambda一样,精准移除对应的那个矩形了。
小额外提示
你的代码里每次MouseMove都新建Random Rand = new Random();其实有坑——Random默认用系统时钟当种子,短时间内多次创建会导致生成的随机数重复。建议把Random实例提到类的字段里,比如:
// 把Random定义成类的成员变量,只初始化一次 private Random _rand = new Random();
然后在方法里直接用_rand.Next(256)就行,这样生成的颜色会更随机~
内容的提问来源于stack exchange,提问作者Himanshu Mange

