关于C#闭包


先看程序:
最后areaName[0],areaName[1],areaName[2]的值都为"黄浦区_上海"。

这个就能得到想要的结果。

 

我的帖子:

http://topic.youkuaiyun.com/u/20091214/23/dddde3da-a064-436a-bf47-830dc80b7690.html

 

还没完全弄明白,不敢下定论,先留位。

 

类似问题的文章及讨论:

http://topic.youkuaiyun.com/u/20090821/13/7c30e8cb-3d37-4d4f-9c11-0df1dd7be8f4.html

http://www.cnblogs.com/foundation/archive/2008/12/11/1352797.html

http://www.cnblogs.com/qishichang/archive/2009/06/20/1507540.html

http://www.cnblogs.com/Klesh/archive/2008/05/15/The-Beauty-of-Closures.html

http://www.kuqin.com/dotnet/20080421/7169.html 

 

比较赞同第二个链接中的观点:

      “ 若匿名方法中引用了某个变量,则该局部变量将被提升为实例变量,并储存于一个叫做闭包(closure)的对象中。提升之后,即使创建该变量的方法执行完毕该变量仍不会消亡。当指向该匿名函数的所有引用都消失后,该闭包变量即可正常地被垃圾回收器回收。闭包中参数或内部变量不是放在栈中,而是放在程序执行过程之中的一张全局表里。

 

       局部变量是通过变成对象的实例字段延长的生命期。编译器自动生成嵌套类,并把局部变量的值给了嵌套类的对象,等方法调用的时候,再从对象中取出来。”

 

补充:

 

        srcStr.Split(',').Where(a => a.Contains(citysArr[temp])).Distinct();  就是一个匿名方法,之所以使用temp可以得到正确的结果,是因为temp每次都重新开辟空间。

 

什么是闭包? 

 

        简单来讲,闭包允许你将一些行为封装,将它像一个对象一样传来递去,而且它依然能够访问到原来第一次声明时的上下文。这样可以使控制结构、逻辑操作等从调用细节中分离出来。访问原来上下文的能力是闭包区别一般对象的重要特征,尽管在实现上只是多了一些编译器技巧。

<think>好的,用户让我介绍一下C#中的闭包。首先,我需要确定用户对闭包的基本了解程度。可能他已经有编程经验,但需要更深入的解释。闭包C#中确实是一个比较重要的概念,尤其是在处理委托和匿名函数时常见。 首先,我应该从闭包的定义开始。闭包是指能够捕获其所在作用域中变量的函数,即使该作用域已经执行完毕。这个概念在函数式编程中很常见,C#通过匿名方法和Lambda表达式支持闭包。 接下来,需要解释闭包如何工作。当在Lambda或匿名函数中使用外部变量时,编译器会自动生成一个类来封装这些变量,这样即使外部方法执行完毕,这些变量仍然存在,因为闭包持有它们的引用。这可能导致一些常见的陷阱,比如循环变量捕获的问题,这时候变量是共享的,可能导致意外结果。 然后,应该举一个例子来说明闭包的基本使用。比如,创建一个函数返回另一个函数,后者访问外部变量。这样用户可以看到闭包如何维持变量的状态。例如,一个计数器,每次调用闭包时递增。 另外,需要提到闭包在延迟执行和异步编程中的应用,比如在Task中使用闭包可能导致变量值不符合预期的情况,这时候需要创建局部变量副本来解决。 还要注意用户可能的疑问点,比如闭包的生命周期和变量捕获的机制。需要解释编译器生成的类和变量提升的过程,以及闭包如何延长变量的生命周期。 最后,总结闭包的优缺点,强调正确使用闭包的重要性,避免内存泄漏和逻辑错误。可能还需要提醒用户在实际编码中注意变量作用域和捕获时机。 现在,我需要把这些点组织成结构清晰的回答,确保符合用户的要求,使用中文,并且正确使用LaTeX格式。检查是否有需要补充的地方,比如更多示例或常见错误场景,确保用户能逐步理解闭包的概念和应用。</think>在C#中,闭包(Closure)是函数与其引用环境组合的编程概念,以下是逐步解析: ### 一、闭包的本质 当Lambda表达式或匿名函数捕获外部作用域的变量时,编译器会自动生成一个隐藏类(例如`<>c__DisplayClass`)来封装被捕获变量。例如: ```csharp Func<int> CreateCounter() { int count = 0; return () => ++count; // 闭包捕获count变量 } ``` 编译后等价于: ```csharp class ClosureClass { public int count; public int Method() => ++count; } Func<int> CreateCounter() { var closure = new ClosureClass(); closure.count = 0; return closure.Method; } ``` ### 二、变量生命周期延长 闭包会将被捕获变量从栈内存提升到堆内存,使得外部方法执行结束后变量仍存在: ```csharp var counter = CreateCounter(); Console.WriteLine(counter()); // 输出1 Console.WriteLine(counter()); // 输出2(变量未被销毁) ``` ### 三、典型陷阱与解决方案 #### 1. 循环变量捕获问题 错误示例: ```csharp var actions = new List<Action>(); for (int i=0; i<3; i++) actions.Add(() => Console.Write(i)); // 所有闭包共享i的最终值2 foreach(var a in actions) a(); // 输出222 ``` 正确做法: ```csharp for (int i=0; i<3; i++) { int temp = i; // 创建临时变量副本 actions.Add(() => Console.Write(temp)); // 输出012 } ``` #### 2. 异步闭包问题 错误示例: ```csharp for (var i=0; i<5; i++) Task.Run(() => Console.Write(i)); // 可能输出55555 ``` 正确做法: ```csharp for (var i=0; i<5; i++) { int copy = i; Task.Run(() => Console.Write(copy)); // 输出01234 } ``` ### 四、闭包的实际应用 1. **状态保持**:实现计数器工厂 ```csharp Func<int> CreateStepCounter(int step) { int total = 0; return () => total += step; } var counter = CreateStepCounter(5); counter(); // 返回5 counter(); // 返回10 ``` 2. **配置延迟执行**: ```csharp var logger = (string prefix) => (string msg) => Console.WriteLine($"{prefix}: {DateTime.Now:HH:mm:ss} - {msg}"); var errorLog = logger("[ERROR]"); errorLog("File not found"); // 输出带统一前缀和时间戳的日志 ``` ### 五、性能注意事项 - 每个闭包会产生约24字节的堆内存开销(包含对象头) - 避免在热点路径中频繁创建闭包 - 对于高性能场景可手动重构为显式类 闭包C#实现函数式编程特性的核心机制,正确理解其实现原理可以避免常见陷阱,并灵活运用于状态封装、回调处理等场景。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值