【发布时间】:2021-04-30 23:10:32
【问题描述】:
所以我发现了这种奇怪之处,我的代码行为会根据我在 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":
- 日志:“cat0”
- 日志:“cat1”
- 日志:“cat2”
这是预期行为,因为 MyCounter 是有状态的。 但是,Example02 会给出以下输出:
- 日志:“cat0”
- 日志:“cat0”
- 日志:“cat0”
每次调用该方法时都会重置 _counter 成员变量...这就像 myCounter 是按值传递的,并且每次在 Example02 中使用时都会重新实例化,而不是像第一个示例中那样放入闭包对象中 - - 像预期的那样。
对此有什么解释吗?我猜这是已知行为而不是错误?一定有一些关于闭包的东西我不知道。这让我非常头疼,因为我从没想过行为会根据我分配闭包参数的位置而改变。
谢谢!
【问题讨论】:
-
你的代码不完整,action在哪里调用?
lambdaFunction = (args) => IncrementAndPrint(args, new MyCounter());所以每次调用都会创建一个新实例。我不知道你为什么觉得这令人惊讶.... -
我真正关心的是为什么你认为它们应该是一样的。每次调用
lambdaFunction时,您要么在 (Example01) 中创建一个实例,要么创建一个新实例 (Example02)... 可以看到为什么即使您只是静态调用此方法也会得到不同的结果?IncrementAndPrint("cat", myCounterLocalInstance)或IncrementAndPrint("cat", new MyCounter()); -
new MyCounter()在每次调用时被调用。每次都会有一个新的。 -
嗯好吧,这是有道理的。我只是认为,当您创建一个 lambda 时,它最初会将其所有依赖项作为成员存储在一个对象中,然后将它们传递给您放入的任何方法。但是,当然它不仅仅是存储成员和一个函数,它正在创建一个全新的功能。我想我只是忘记了它可以做到这一点,在我使用它们来重载一个方法的情况下。谢谢!
标签: c# lambda closures parameter-passing