为了账号安全,请及时绑定邮箱和手机立即绑定

在lambda表达式中使用foreach循环的迭代器变量-为什么失败?

在lambda表达式中使用foreach循环的迭代器变量-为什么失败?

C#
守候你守候我 2019-11-02 10:11:20
考虑以下代码:public class MyClass{   public delegate string PrintHelloType(string greeting);    public void Execute()    {        Type[] types = new Type[] { typeof(string), typeof(float), typeof(int)};        List<PrintHelloType> helloMethods = new List<PrintHelloType>();        foreach (var type in types)        {            var sayHello =                 new PrintHelloType(greeting => SayGreetingToType(type, greeting));            helloMethods.Add(sayHello);        }        foreach (var helloMethod in helloMethods)        {            Console.WriteLine(helloMethod("Hi"));        }    }    public string SayGreetingToType(Type type, string greetingText)    {        return greetingText + " " + type.Name;    }...}调用之后myClass.Execute(),代码将输出以下意外响应:嗨Int32嗨Int32嗨Int32  很显然,我希望"Hi String","Hi Single","Hi Int32",但显然并非如此。为什么在所有3种方法中都使用了迭代数组的最后一个元素而不是适当的方法?您将如何重写代码以实现期望的目标?
查看完整描述

3 回答

?
缥缈止盈

TA贡献2041条经验 获得超4个赞

欢迎来到闭包和捕获变量的世界:)


埃里克·利珀特(Eric Lippert)对这种行为有深入的解释:


关闭循环变量被认为是有害的

关闭循环变量,第二部分

基本上,捕获的是循环变量,而不是值。要获得您认为应该获得的东西,请执行以下操作:


foreach (var type in types)

{

   var newType = type;

   var sayHello = 

            new PrintHelloType(greeting => SayGreetingToType(newType, greeting));

   helloMethods.Add(sayHello);

}


查看完整回答
反对 回复 2019-11-02
?
Qyouu

TA贡献1786条经验 获得超11个赞

作为暗示SWeko引用的博客文章的简要说明,lambda捕获变量而不是value。在foreach循环中,变量在每次迭代中都不是唯一的,在循环期间使用相同的变量(当您看到编译器在编译时对foreach执行的扩展时,这一点更加明显)。结果,您在每次迭代中都捕获了相同的变量,并且该变量(截至上次迭代)指的是集合中的最后一个元素。

更新:在较新版本的语言(从C#5开始)中,循环变量在每次迭代中都被视为新变量,因此关闭它不会产生与较早版本(C#4和更低版本)相同的问题。


查看完整回答
反对 回复 2019-11-02
?
隔江千里

TA贡献1906条经验 获得超10个赞

您可以通过引入其他变量来修复它:


...

foreach (var type in types)

        {

            var t = type;

            var sayHello = new PrintHelloType(greeting => SayGreetingToType(t, greeting));

            helloMethods.Add(sayHello);

        }

....


查看完整回答
反对 回复 2019-11-02
  • 3 回答
  • 0 关注
  • 715 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信