8wDlpd.png
8wDFp9.png
8wDEOx.png
8wDMfH.png
8wDKte.png

在 C# 中的循环中捕获变量

Evariste Galois 2月前

222 0

我遇到了一个关于 C# 的有趣问题。我有如下代码。列表 >actions=新列表 >();int 变量 = 0;while (变量 < 5){actions.Add((...

我遇到了一个有关 C# 的有趣问题。我有如下代码。

List<Func<int>> actions = new List<Func<int>>();

int variable = 0;
while (variable < 5)
{
    actions.Add(() => variable * 2);
    ++ variable;
}

foreach (var act in actions)
{
    Console.WriteLine(act.Invoke());
}

我希望它输出 0、2、4、6、8。但是,它实际上输出了五个 10。

看起来这是因为所有操作都引用一个捕获的变量。因此,当它们被调用时,它们都有相同的输出。

有没有办法解决这个限制,让每个动作实例都有自己的捕获变量?

帖子版权声明 1、本帖标题:在 C# 中的循环中捕获变量
    本站网址:http://xjnalaquan.com/
2、本网站的资源部分来源于网络,如有侵权,请联系站长进行删除处理。
3、会员发帖仅代表会员个人观点,并不代表本站赞同其观点和对其真实性负责。
4、本站一律禁止以任何方式发布或转载任何违法的相关信息,访客发现请向站长举报
5、站长邮箱:yeweds@126.com 除非注明,本帖由Evariste Galois在本站《multithreading》版块原创发布, 转载请注明出处!
最新回复 (0)
  • 这与循环无关。

    触发此行为是因为您使用了 lambda 表达式, () => variable * 2 但其外部作用域 variable 并未在 lambda 的内部作用域中实际定义。

    Lambda 表达式(在 C#3+ 中,以及在 C#2 中为匿名方法)仍会创建实际方法。将变量传递给这些方法会遇到一些难题(按值传递?按引用传递?C# 采用按引用传递 - 但这又带来了另一个问题,即引用可能比实际变量存在的时间更长)。C# 解决所有这些难题的方法是创建一个新的辅助类(“闭包”),其中字段对应于 lambda 表达式中使用的局部变量,方法对应于实际的 lambda 方法。 variable 代码中的任何更改实际上都会转化为该代码中的更改 ClosureClass.variable

    因此,您的 while 循环会不断更新, ClosureClass.variable 直到达到 10,然后您的 for 循环会执行所有操作,这些操作都在同一操作上进行 ClosureClass.variable .

    为了获得预期结果,您需要在循环变量和要关闭的变量之间创建分隔。您可以通过引入另一个变量来实现这一点,即:

    List<Func<int>> actions = new List<Func<int>>();
    int variable = 0;
    while (variable < 5)
    {
        var t = variable; // now t will be closured (i.e. replaced by a field in the new class)
        actions.Add(() => t * 2);
        ++variable; // changing variable won't affect the closured variable t
    }
    foreach (var act in actions)
    {
        Console.WriteLine(act.Invoke());
    }
    

    您还可以将闭包移至另一种方法来创建这种分离:

    List<Func<int>> actions = new List<Func<int>>();
    
    int variable = 0;
    while (variable < 5)
    {
        actions.Add(Mult(variable));
        ++variable;
    }
    
    foreach (var act in actions)
    {
        Console.WriteLine(act.Invoke());
    }
    

    您可以将 Mult 实现为 lambda 表达式(隐式闭包)

    static Func<int> Mult(int i)
    {
        return () => i * 2;
    }
    

    或者使用实际的辅助类:

    public class Helper
    {
        public int _i;
        public Helper(int i)
        {
            _i = i;
        }
        public int Method()
        {
            return _i * 2;
        }
    }
    
    static Func<int> Mult(int i)
    {
        Helper help = new Helper(i);
        return help.Method;
    }
    

    无论如何, “闭包”并不是一个与循环相关的概念 ,而是与匿名方法/ lambda 表达式使用局部范围变量有关 - 尽管一些不谨慎的循环使用会展示闭包陷阱。

返回
作者最近主题: