繁体   English   中英

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

[英]Using the iterator variable of foreach loop in a lambda expression - why fails?

考虑以下代码:

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() ,代码将输出以下意外响应:

Hi Int32
Hi Int32
Hi Int32

显然,我希望使用"Hi String""Hi Single""Hi Int32" ,但显然并非如此。 为什么在所有3种方法中都使用了迭代数组的最后一个元素而不是适当的方法?

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

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

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

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

foreach (var type in types)
{
   var newType = type;
   var sayHello = 
            new PrintHelloType(greeting => SayGreetingToType(newType, greeting));
   helloMethods.Add(sayHello);
}

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

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

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

...
foreach (var type in types)
        {
            var t = type;
            var sayHello = new PrintHelloType(greeting => SayGreetingToType(t, greeting));
            helloMethods.Add(sayHello);
        }
....

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM