如何避免lambda表达式中的变量捕获?

时间:2011-04-06 22:42:28

标签: c# lambda


最近我在深度阅读了C#,它教会了我关于lambda表达式的信息,我一直用它来将数据传递给点击事件,例如:

image.MouseDown+=(o ,e)=>MethodToDoSomething(DataNeededForAction);

现在问题是在foreach循环中使用变量捕获(感谢Jon Skeet使这部分非常清楚:),当初始化具有我订阅的事件的几个对象时我通常遇到变量问题捕获。请考虑以下示例:

foreach (var game in GamesCollection)
{
    Image img = new Image();
    img.Width = 100;
    img.MouseDown+=(o,e) => MyMethod(game.id);
}

为了避免在这种情况下捕获,我必须添加一些变量来分配游戏,然后将该变量传递给方法,这会产生额外的不清楚的代码,并且主要是额外的混乱。 有没有办法绕过这个?什么东西至少看起来更干净?

Thx,Ziv

4 个答案:

答案 0 :(得分:5)

(编辑:请注意,这已经改变了C#5,其中foreach现在有效地为每次迭代创建一个新变量。)

不,没有办法避免这种情况。基本上,语言规范在单个变量方面描述foreach的方式是错误的,而不是每次迭代的新变量。就个人所涉及的代码数量而言,我个人并不认为这是一个问题 - 当你意识到这是一个问题并想出如何修复它时,你已经克服了最大的障碍。我想通常会包含一条注释,让维护程序员明白这一点。

我确信如果他们从头开始,C#团队会采用不同的方式,但事实并非如此。哎呀,they've even discussed changing the existing behaviour ......但是有很好的理由反对这个改变(特别是那些在C#N + 1编译器上正常工作的代码仍然可以编译,但是给出了错误的结果C#N编译器;开源库作者希望他们的代码使用多个编译器构建的一个非常微妙的bug源。)

(希望你喜欢这本书,顺便说一句......)

答案 1 :(得分:2)

简而言之,没有。您需要创建额外的存储空间,以便在方法闭包中保持正确的game实例。

答案 2 :(得分:0)

如果GamesCollectionList<T>,您可以使用ForEach方法:

GamesCollection.ForEach(game => {
    Image img = new Image();
    img.Width = 100;
    img.MouseDown+=(o,e) => MyMethod(game.id);
});

但总的来说,没有。还有一个额外的变量并不是那么混乱。

答案 3 :(得分:-1)

如何使用LINQ表达式首先拔出game.id。通过.Aggregate()推送调用应该每次都创建一个新变量,因为它实际上是一个方法参数。

GamesCollection.Select(game => game.id).Aggregate((_, id) => {
    Image img = new Image();
    img.Width = 100;
    img.MouseDown+=(o,e) => MyMethod(id);
    return img;
});