闭包的由来

要说闭包的由来就不得不先说下函数式编程了。近几年函数式编程也是比较火热,我们先来看看函数式编程的一些基本的特性这个有助于我们理解闭包的由来。

函数式编程

函数式编程是一种编程模型,他将计算机运算看做是数学中函数的计算,并且避免了状态以及变量的概念。这里很明显的指出了函数式编程中最重要的就是函数而且是数学中的函数,比如f(x),数学中的函数最大的特点就是只要是同样的参数x那么我的结果必定是相等的,也就是说我们函数的返回值只是依赖于参数而不依赖于其他状态(比如js中的全局变量就是一个干扰因素);后一句中说避免变量的概念,这句话如果从函数式编程来说不太恰当,因为这句话中的函数意思还是我们在编程语言中所使用的变量也就是一个存储单元,而在函数式编程中变量却是数学中变量的定义是一个值得名称。比如,我们最基本的赋值等式 x = x+1,让我们程序员看这是一个简单的赋值代码,而让学数学的人来说这个等式是根本不成立的。 所以我们在函数式编程中是不允许多次赋值的。而这一句话也是讲述了函数式编程好处的最主要的原因:

第一点、函数的结果只依赖于参数而不依赖其他状态,这样写的代码很容易进行推理不容易发生错误,极大的方便的单元测试和调试。

第二点、因为不可变性和无状态,那么我们在处理多个线程之间就不用担心资源的争夺,不需要用锁来保存状态。

高阶函数

函数式编程中函数是一等公民,和我们的口号 "万物皆对象"有点相似,在函数式编程中,我们努力用函数来表达所有的事情,当然我们也需要函数可以传过来传过去这就是高阶函数,也就是把函数作为参数或者返回值,继而实现复用,这样即是可以把复用的粒度降到函数。C#语言中也有类似的东西--委托,当然C#中的函数跟函数式编程中的就不一样了,但是有吸收一些函数式编程语言中的特性,比如C#中lamda,Linq。

关于函数式编程博客园有很多很好的文章介绍我就不详说了,接下来就是引出我们今天的主题--闭包。

因为函数式编程的基础就是Lambda演算,所以这一节演算我们用Lambda演算来带出我们的主题,关于这个演算我也懂得不是太多,想要入门的同学可以看看这个  点这里

首先定义一个简单的演算

λx.λy.x+y

如果x为1 y为2 演算过程则为

((λx.λy.x+y)1)2=(λy.1+y)2=(1+2)=3

接下来我们用到高阶函数

λy . (λx . x + y)

演算过程:

((λy . (λx . x + y))1)2=((λx . x + 1))2 = (2+1)=3

可以看到这个演算中外层函数使用的是内层函数,也就是说使用是一个函数作为了计算结果。OK ,我们把内层函数单独拿出来,(λx . x + y),可以很明显的看到如果脱离的上下文呢,我们的y是没有值的,也就是y是没有绑定的,也可以说y对于我们这个函数是自由的! 如果在函数式编程中,在外层函数执行完毕之后我们的y变量就应该被销毁,那么如果我们在内层函数中如果还需要用到y的话怎么办呢? 对于这个问题,设计者则做了其他的处理:如果一个函数返回另一个函数,而被返回函数又需要外层函数的变量时,不会立即释放这个变量,而是允许被返回的函数引用这些变量。支持这种机制的语言称为支持闭包机制,而这个内部函数连同其自由变量就形成了一个闭包(这句话是摘自其他博客,自己难得整理文字。。。)。这就是我们闭包的由来,而我们其他的语言如果有用到函数式编程的思想,并且允许函数来进行传递就会遇到类似的问题,所以各个语言就需要用其自己的方式来实现闭包!

C#中闭包的实现

从上一节我们也就是能总结出闭包其实就是要执行并且包含自由变量的代码块(由于自由变量被包含在代码块中,这些自由变量以及它们引用的对象没有被释放)和为自由变量提供绑定的计算环境的一个结合。

然后进入我们的C#编程时刻了,我们就用简单的例子来实现,并且查看编译器生成的代码 看看C#中是怎么实现闭包,毕竟我们也是有委托的 。。。

首先写一个简单得不能在简单的代码

 using System;

 namespace closure
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine(test()());
Console.ReadKey();
} public static Func<int,int> test(int x)
{
//作用域1
return (y) =>
{
//作用域2
return x + y;
};
}
}
}

可以看到我们test的方法中传入变量x的作用域是在1 在执行匿名函数的时候应该是已经释放在作用域2就不应该存在了,而我们却能准确的得到计算结果

说明我们的变量x确实在作用域2中还存在,接下来我们看看编译器帮我们做了什么事情,

可以看到我们的test方法中多了一个对象 <>c__DisplayClass1_0  class_;这个东西的具体定义是啥?

这个很明显了,其实闭包只是编译器帮我们把自由变量封装到了一个对象中供我们作用域外使用,那我们如果去掉作用域2中使用x变量呢?

编译器原来为了自由变量维护的对象没了 。。。结果在意料之中。

OK,这篇文章就到此结束了,关于闭包是python中啊 js中啊 或者C#中得用处我就不细说了,博客园中有太多太多的介绍了,希望这边文章对你有帮助,欢迎拍砖!

C#温故而知新系列 -- 闭包的更多相关文章

  1. sql server 基础教程[温故而知新三]

    子曰:“温故而知新,可以为师矣.”孔子说:“温习旧知识从而得知新的理解与体会,凭借这一点就可以成为老师了.“ 尤其是咱们搞程序的人,不管是不是全栈工程师,都是集十八般武艺于一身.不过有时候有些知识如果 ...

  2. jquery 基础教程[温故而知新二]

    子曰:“温故而知新,可以为师矣.”孔子说:“温习旧知识从而得知新的理解与体会,凭借这一点就可以成为老师了.“ 尤其是咱们搞程序的人,不管是不是全栈工程师,都是集十八般武艺于一身.不过有时候有些知识如果 ...

  3. javascript 基础教程[温故而知新一]

    子曰:“温故而知新,可以为师矣.”孔子说:“温习旧知识从而得知新的理解与体会,凭借这一点就可以成为老师了.“ 尤其是咱们搞程序的人,不管是不是全栈工程师,都是集十八般武艺于一身.不过有时候有些知识如果 ...

  4. Spring MVC (二)注解式开发使用详解

    MVC注解式开发即处理器基于注解的类开发, 对于每一个定义的处理器, 无需在xml中注册. 只需在代码中通过对类与方法的注解, 即可完成注册. 定义处理器 @Controller: 当前类为处理器 @ ...

  5. SQLServer 用法简例

      子曰:“温故而知新,可以为师矣.”孔子说:“温习旧知识从而得知新的理解与体会,凭借这一点就可以成为老师了.“ 尤其是咱们搞程序的人,不管是不是全栈工程师,都是集十八般武艺于一身.不过有时候有些知识 ...

  6. 深入理解javascript原型和闭包系列

    从下面目录中可以看到,本系列有16篇文章,外加两篇后补的,一共18篇文章.写了半个月,从9月17号开始写的.每篇文章更新时,读者的反馈还是可以的,虽然不至于上头条,但是也算是中规中矩,有看的人,也有评 ...

  7. JavaScript 闭包系列二(匿名函数及函数的闭包)

    一. 匿名函数 1. 函数的定义,可分为三种 1) 函数声明方式 function double(x) {     return 2*x; } 2)Function构造函数,把参数列表和函数体都作为字 ...

  8. 《深入理解javascript原型和闭包系列》 知识点整理(转)

    深入理解javascript原型和闭包系列 对原型和闭包等相关知识的讲解,由浅入深,通俗易懂,每个字都值得细细研究. 一.一切都是对象 1. typeof操作符输出6种类型:string boolea ...

  9. 《深入理解javascript原型和闭包系列》 知识点整理

    深入理解javascript原型和闭包系列 对原型和闭包等相关知识的讲解,由浅入深,通俗易懂,每个字都值得细细研究. 一.一切都是对象 1. typeof操作符输出6种类型:string boolea ...

随机推荐

  1. Linux多线程编程初探

    Linux线程介绍 进程与线程 典型的UNIX/Linux进程可以看成只有一个控制线程:一个进程在同一时刻只做一件事情.有了多个控制线程后,在程序设计时可以把进程设计成在同一时刻做不止一件事,每个线程 ...

  2. 一张图了解cocos2d坐标系

    一张图了解cocos2d坐标系 平面直角坐标系

  3. win32 线程通信初步

    // 线程通信机制.cpp : 定义控制台应用程序的入口点. // #include "stdafx.h" #define NUM_THREADS 10 #include < ...

  4. JQuery UI - resizable

     ·概述 resizable插件可以让选中的元素具有改变尺寸的功能. 官方示例地址:http://jqueryui.com/demos/resizable/ 所有的事件回调函数都有两个参数:eve ...

  5. 收集一些dos网络配置命令,从新获取ip刷新dns

    今天台式机插着无线网卡连接学校无线网,结果就要登录了,完后ip地址固定半天,换了mac地址重新分配还是不能改,ping了主机也不通,我想可能是dns没有刷新.现在收集几个dos命令用来配置网络: 1. ...

  6. 面试之路(28)-反转链表(reverse ListNode)

    反转链表: java类 public class ListNode{ int key; ListNode next; } 思路分析: 需要三个指针,current,prev和next. current ...

  7. 关于linux内核驱动开发中Makefile编译的问题

    obj-y:打个比方,我要编译的是hello.c这个文件,obj-y就会把hello.c或者hello.c编译生成的hello.s文件链接到内核中去. obj-m:打个比方,我要编译的是hello.c ...

  8. Spring 学习笔记---Bean的生命周期

    生命周期图解 由于Bean的生命周期经历的阶段比较多,我们将通过一个图形化的方式进行描述.下图描述了BeanFactory中Bean生命周期的完整过程: Bean 的生命周期从Spring容器着手实例 ...

  9. JAVA物联网九大核心热点技术

    1. Unix/Linux平台技术: Unix系统原理.Unix常用命令.Shell编程.  2. Java EE核心技术: Java语言核心.Java高级API.JVM及性能优化.Java Secu ...

  10. P3370 【模板】字符串哈希

    题目描述 如题,给定N个字符串(第i个字符串长度为Mi,字符串内包含数字.大小写字母,大小写敏感),请求出N个字符串中共有多少个不同的字符串. 输入输出格式 输入格式: 第一行包含一个整数N,为字符串 ...