闭包定义

闭包(closure)在很多语言中都存在,在C#中,闭包是由匿名函数来表示的。C#中的闭包也叫做捕获的变量。当一个匿名函数引用了他所在作用域(一般情况下是一个方法)的局部变量时,为了能够顺利的执行匿名函数而不至于包含它的函数执行完之后线程栈弹出导致局部变量消失,会将这个变量的生命周期延长。这时就形成了闭包。闭包利用了匿名函数的一个特性:因为编译器会为匿名函数生成一个类(或结构),所以,提升匿名函数捕获的这个变量的生命周期的方法就是在把这个变量放到这个类中。此外,这个类中定义的方法既是这个匿名函数。

示例

for循环中的闭包陷阱

我们在使用lambda的时候会遇到闭包,在闭包中有一个陷阱是在for循环中产生的,先上代码:

class Program
{
static void Main(string[] args)
{
Action[] actions=new Action[];
for (int i = ; i < actions.Length; i++)
{
actions[i] = () => Console.WriteLine(i);
}
foreach (var item in actions)
{
item();
}
Console.ReadKey();
}
}

此时会看到Console输出的是一连串的5,这是因为C#中在for块中定义的int i会被当作外部变量来处理,我们在循环内部使用lambda的时候编译器会给我们生成一个类,比如这个代码如果是在Program中的main方法执行的时候这个类会在Program中生成,成为Program的一个内部类。这个类的主要作用是承载lambda表达式所代表的方法。当lambda表达式引用了一个局部变量时,为了保证这个变量的生命周期,这个局部变量会被编译器生成的这个类所捕捉,也就是说,这个局部变量的生命周期得到了提升,成为了一个类级别的字段了。

模拟闭包

我想说的是如何避免这个for循环中闭包的陷阱呢?先来模拟一下编译器在lambda背后的行为:

class Program
{
static void Main(string[] args)
{
Action[] actions = new Action[];
var innerClass = new InnerClass();//关键在这里
int i;//for循环中定义的局部变量是被当作外部变量来使用的,这是在C#中的实现。
for (i = ; i < actions.Length; i++)
{
innerClass.i = i;
actions[i] = innerClass.DoIt;
}
foreach (var item in actions)
{ item();
}
Console.ReadKey();
} public class InnerClass//这里是模拟编译器为lambda表达式生成的类,我暂时命名为InnerClass,实际上编译器生成的这个内部类有自己的命名规则。
{
public int i;//这个是捕获的for循环中的那个变量。 public void DoIt()
{
Console.WriteLine(i);
}
} }

闭包产生的这个陷阱关键就在于:

 var innerClass = new InnerClass();//关键在这里

避免闭包陷阱

上面这句代码的位置,之所以会产生陷阱,就是因为innerClass捕获到的是最后的那个变量i的值,说到这里就不难想象如何去避免这个陷阱了,我们可以在for循环内部定义一个变量来保存每次的循环变量i的值:

 class Program
{
static void Main(string[] args)
{
Action[] actions = new Action[]; for (int i = ; i < actions.Length; i++)
{
int j = i;//关键这里
actions[i] = Console.WriteLine(j);
}
foreach (var item in actions)
{ item();
}
Console.ReadKey();
}
}

我们在for循环的内部使用了一个变量先来捕获一遍i,然后编译器会将这个生成的类放在循环内部(而不是在for循环外部生成),每循环一次就生成一个新的来捕获。牛逼吧编译器?

C#中闭包的陷阱的更多相关文章

  1. JavaScript中的this陷阱的最全收集 没有之一

    当有人问起你JavaScript有什么特点的时候,你可能立马就想到了单线程.事件驱动.面向对象等一堆词语,但是如果真的让你解释一下这些概 念,可能真解释不清楚.有句话这么说:如果你不能向一个6岁小孩解 ...

  2. 转:JavaScript中的this陷阱的最全收集

    在其他地方看到的,觉得解释的狠详细,特此分享 当有人问起你JavaScript有什么特点的时候,你可能立马就想到了单线程.事件驱动.面向对象等一堆词语,但是如果真的让你解释一下这些概念,可能真解释不清 ...

  3. JavaScript中的this陷阱

    当有人问起你JavaScript有什么特点的时候,你可能立马就想到了单线程.事件驱动.面向对象等一堆词语,但是如果真的让你解释一下这些概念,可能真解释不清楚.有句话这么说:如果你不能向一个6岁小孩解释 ...

  4. 关于js中闭包的理解

    1.以前很不理解js中闭包的概念及使用,下面来看一下 function foo() { var a = 123; var b = 456; return function () { return a; ...

  5. 彻底搞清js中闭包(Closure)的概念

    js中闭包这个概念对于初学js的同学来说, 会比较陌生, 有些难以理解, 理解起来非常模糊. 今天就和大家一起来探讨一下这个玩意. 相信大家在看完后, 心中的迷惑会迎然而解. 闭包概念: 闭包就是有权 ...

  6. HashTable和HashSet中的类型陷阱

    HashTable和HashSet中的类型陷阱 发现这个陷阱的起因是这样的:我现在有上百万字符串,我准备用TopK算法统计出出现次数做多的前100个字符串. 首先我用Hashtable统计出了每个字符 ...

  7. 在Javascript中闭包(Closure)

    在Javascript中闭包(Closure) 什么是闭包 “官方”的解释是:所谓“闭包”,指的是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分. ...

  8. 对JavaScript中闭包的理解

    在前端开发中闭包是一个很重要的知识点,是面试中一定会被问到的内容.之前我对闭包的理解主要是"通过闭包可以在函数外部能访问到函数内部的变量",对闭包运用的也很少,甚至自己写过闭包自己 ...

  9. 关于JS中闭包的问题

    一直以来,我都以为我已经懂了JavaScript中闭包的概念,直到有一次小伙伴突然问我这个概念的时候,我才发现我根本不知道该怎来么跟他来讲述这个概念. 那时候我就知道我是自我欺骗,打肿脸充胖子了. 所 ...

随机推荐

  1. 【Beta】博客合集

    [Beta Scrum]冲刺! 1/5 [Beta Scrum]冲刺! 2/5 [Beta Scrum]冲刺! 3/5 [Beta Scrum]冲刺! 4/5 [Beta Scrum]冲刺! 5/5

  2. chrome的source工具

    http://www.cnblogs.com/ys-ys/p/5597717.html http://www.cnblogs.com/strick/p/5556434.html

  3. (转)Spring Boot 2 (四):使用 Docker 部署 Spring Boot

    http://www.ityouknow.com/springboot/2018/03/19/spring-boot-docker.html Docker 技术发展为微服务落地提供了更加便利的环境,使 ...

  4. C#多线程编程のSemaphore(信号量,负责协调各个线程)

    Semaphore负责协调线程,可以限制对某一资源访问的线程数量 这里对SemaphoreSlim类的用法做一个简单的例子: namespace WpfApplication6 { /// <s ...

  5. ElasticSearch(五):Java操作ElasticSearch执行查询

    package com.gxy.ESChap01; import java.net.InetAddress; import org.elasticsearch.action.search.Search ...

  6. Android中实现短信发送的一种方式

    SendSmsActivity.java: package com.test.smsmangerdemo.sendsmsactivity; import android.support.v7.app. ...

  7. Pyhon流程控制

    1.条件控制 Python 中用 elif 代替了 else if,所以if语句的关键字为:if – elif – else. 注意: 1.每个条件后面要使用冒号 :,表示接下来是满足条件后要执行的语 ...

  8. 如果IBM再给我一次实习机会

    2014年,我拿到了IBM斯图加特R&D的实习机会.在连续被索尼和博世拒掉之后,这个实习对我来说弥足珍贵.我学的是通信专业,在这之前与编程相关的活动只有一学期的安卓Lab,还是靠抱队友大腿才及 ...

  9. 使用C#创建SQLite控制台应用程序

    本文属于原创,转载请注明出处,谢谢! 一.开发环境 操作系统:Windows 10 X64 开发环境:VS2015 编程语言:C# .NET版本:.NET Framework 4.0 目标平台:X86 ...

  10. java连接Mysql8

    相较于之前版本会有部分改动 pom依赖 <dependency> <groupId>mysql</groupId> <artifactId>mysql- ...