首页 > 解决方案 > 如果在 lambda 之前实例化对象而不是在 lambda 内部实例化对象,C# 闭包行为会发生变化吗?

问题描述

所以我发现了这种奇怪之处,我的代码行为会根据我在 lambda 函数中分配将“关闭”的对象的位置而改变。

这是我所经历的简化示例


Example01(){
    var myCounterLocalInstance = new MyCounter(); 
    Action<string> lambdaFunction = (args) => IncrementAndPrint(args, myCounterLocalInstance);
}

Example02(){
   Action<string> lambdaFunction = (args) => IncrementAndPrint(args, new MyCounter());
}

IncrementAndPrint(string args, MyCounter counter){
   Console.WriteLine(args + counter.GetValueAndCount());
}

class MyCounter
{
   int _counter;
   public int GetValueAndCount() => _counter++;
}

奇怪的是 Example01 和 Example02 在我的案例中实际上并没有给出相同的结果。在 Example01 中,一切都按预期工作,如果 args == "cat",控制台输出将是:

  1. 日志:“cat0”
  2. 日志:“cat1”
  3. 日志:“cat2”

这是预期行为,因为 MyCounter 是有状态的。但是,Example02 会给出以下输出:

  1. 日志:“cat0”
  2. 日志:“cat0”
  3. 日志:“cat0”

每次调用该方法时都会重置 _counter 成员变量...就像 myCounter 是按值传递的,并且每次在 Example02 中使用时都会重新实例化,而不是像第一个示例中那样被放入闭包对象中——就像是预期的。

对此有什么解释吗?我猜这是已知行为而不是错误?一定有一些关于闭包的东西我不知道。这让我非常头疼,因为我从没想过行为会根据我分配闭包参数的位置而改变。

谢谢!

标签: c#lambdaclosuresparameter-passing

解决方案


你在想这个。

在此方法中,您将创建一个 的实例MyCounter,每次调用委托时该实例都会递增。

static void Example01(){
   var myCounterLocalInstance = new MyCounter(); 
   Action<string> lambdaFunction = (args) => IncrementAndPrint(args, myCounterLocalInstance);
   lambdaFunction("cat");
   lambdaFunction("cat");
   lambdaFunction("cat");
   lambdaFunction("cat");
}

MyCounter在此方法中,您每次调用委托时都会创建一个新实例,其计数将始终为 0!

static void Example02(){
   Action<string> lambdaFunction = (args) => IncrementAndPrint(args, new MyCounter());
   lambdaFunction("cat");
   lambdaFunction("cat");
   lambdaFunction("cat");
   lambdaFunction("cat");
}

如果你只是IncrementAndPrint静态调用,你会得到相同的结果:

static void Example01(){
   var myCounterLocalInstance = new MyCounter();
   IncrementAndPrint("cat",myCounterLocalInstance);
   IncrementAndPrint("cat",myCounterLocalInstance);
   IncrementAndPrint("cat",myCounterLocalInstance);
   IncrementAndPrint("cat",myCounterLocalInstance);
}

static void Example02(){
   IncrementAndPrint("cat", new MyCounter());
   IncrementAndPrint("cat", new MyCounter());
   IncrementAndPrint("cat", new MyCounter());
   IncrementAndPrint("cat", new MyCounter());
}

推荐阅读