c# - 如果在 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",控制台输出将是:
- 日志:“cat0”
- 日志:“cat1”
- 日志:“cat2”
这是预期行为,因为 MyCounter 是有状态的。但是,Example02 会给出以下输出:
- 日志:“cat0”
- 日志:“cat0”
- 日志:“cat0”
每次调用该方法时都会重置 _counter 成员变量...就像 myCounter 是按值传递的,并且每次在 Example02 中使用时都会重新实例化,而不是像第一个示例中那样被放入闭包对象中——就像是预期的。
对此有什么解释吗?我猜这是已知行为而不是错误?一定有一些关于闭包的东西我不知道。这让我非常头疼,因为我从没想过行为会根据我分配闭包参数的位置而改变。
谢谢!
解决方案
你在想这个。
在此方法中,您将创建一个 的实例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());
}
推荐阅读
- flask - python看门狗挂烧瓶socketio
- python - 如何导入 dtdata 库?
- python - 读取客户端Python文件
- c# - 多个复选框创建 LINQ
- css - CSS自定义属性放置:root vs body/html标签
- r - R中接受键值对作为参数的函数的最佳实践?
- angular - 在 Angular10 上使用自定义 webpack 遇到异常
- c# - 提高使用 CreateDocumentQuery 和 ExecuteNextAsync 时的性能
- python-3.x - 容量约束检查仅在 OR-Tools 中的节点级别
- java - 根据比较对象的变量组合2个列表