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

考虑以下代码:


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种方法中都使用了迭代数组的最后一个元素而不是适当的方法?


您将如何重写代码以实现期望的目标?


守候你守候我
浏览 716回答 3
3回答

缥缈止盈

欢迎来到闭包和捕获变量的世界:)埃里克·利珀特(Eric Lippert)对这种行为有深入的解释:关闭循环变量被认为是有害的关闭循环变量,第二部分基本上,捕获的是循环变量,而不是值。要获得您认为应该获得的东西,请执行以下操作:foreach (var type in types){&nbsp; &nbsp;var newType = type;&nbsp; &nbsp;var sayHello =&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; new PrintHelloType(greeting => SayGreetingToType(newType, greeting));&nbsp; &nbsp;helloMethods.Add(sayHello);}

Qyouu

作为暗示SWeko引用的博客文章的简要说明,lambda捕获变量而不是value。在foreach循环中,变量在每次迭代中都不是唯一的,在循环期间使用相同的变量(当您看到编译器在编译时对foreach执行的扩展时,这一点更加明显)。结果,您在每次迭代中都捕获了相同的变量,并且该变量(截至上次迭代)指的是集合中的最后一个元素。更新:在较新版本的语言(从C#5开始)中,循环变量在每次迭代中都被视为新变量,因此关闭它不会产生与较早版本(C#4和更低版本)相同的问题。

隔江千里

您可以通过引入其他变量来修复它:...foreach (var type in types)&nbsp; &nbsp; &nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; var t = type;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; var sayHello = new PrintHelloType(greeting => SayGreetingToType(t, greeting));&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; helloMethods.Add(sayHello);&nbsp; &nbsp; &nbsp; &nbsp; }....
打开App,查看更多内容
随时随地看视频慕课网APP