闭包在.NET下的实现(编译器玩的小把戏)-----C#本质论读书笔记

参加工作以来,拜读过很多大作。有好多书读的时候感觉大彻大悟,不自觉的发出‘哦。。。’这样的感叹。但是随着杀猪刀(时间)不断的飞逝,不仅给我脸上带来了皱纹,还带走了不少前人总结和归纳的知识点和那些我还没来得及吸收的宝贵财富。好记性不如烂笔头,我决定开始写一些读书的笔记和工作上的心得,最好是两者能够结合起来,把知识真正的自己转化成自己的能力。

下面进入正题。闭包在程序员的日常工作几乎无所不在,当然不仅是工作,面试的时候经常也会有相关的题目。看过很多博客园上的朋友都有解释,每次重温都有‘哦。。。’的感觉。对,是每次。。。可能是因为我才疏学浅的原因吧,有些大神解释得很深入,最后总结的又太精辟。这几天在重温C#本质论(第三版)(有一个声音飘过:你之前也没看完过。。。。)。里面提到了闭包,只是很小的一段,说的不算‘深入’(没有列出IL),但是绝对算得上浅出,这回我真的是大彻大悟,相信你看完也必须懂了。

为了更好的理解代码,需要用几行代码来说明一下Lamda表达式(匿名方法)的实现。和闭包一样,它们只是编译器玩的小把戏,为的实让我们用起来更爽一点。

class Program
    {
        static void Main(string[] args)
        {
            int[] items = new int[2];
            for (int i = 0; i < items.Length; i++)
            {
                Console.WriteLine("Enter a integer:");
                items[i] = int.Parse(Console.ReadLine());
            }

var flag = Sort(items, (first, second) => { return first > second; });

if (flag)
                Console.WriteLine("First Parameter is bigger");
            else
                Console.WriteLine("Second Parameter is bigger");

Console.Read();
        }

static bool Sort(int[] items, Func<int,int,bool> method)
        {
            return method(items[0], items[1]);
        }
    }

上面一段代码很简单,比较两次输入的大小,其中使用了lambda表达式来传递了比较的方法,那么经过编译器编译之后呢,下面一段代码展示了编译器生产的CIL代码对应的C#代码:

class Program
    {
        static void Main(string[] args)
        {
            int[] items = new int[2];
            for (int i = 0; i < items.Length; i++)
            {
                Console.WriteLine("Enter a integer:");
                items[i] = int.Parse(Console.ReadLine());
            }

var flag = Sort(items,Program.__AnonymousMethod_00000000);

if (flag)
                Console.WriteLine("First Parameter is bigger");
            else
                Console.WriteLine("Second Parameter is bigger");

Console.Read();
        }

static bool Sort(int[] items, Func<int,int,bool> method)
        {
            return method(items[0], items[1]);
        }

   private static bool Program.__AnonymousMethod_00000000(

     int first, int second)

   {

    return first < second;

   }
    }

一目了然,编译器帮我们生成了一个static的函数。可能有人要问,跟闭包有什么关系呢?我们再看一段代码:

class Program
    {
        static void Main(string[] args)
        {
            int comparionCount = 0;
            int[] items = new int[2];
            for (int i = 0; i < items.Length; i++)
            {
                Console.WriteLine("Enter a integer:");
                items[i] = int.Parse(Console.ReadLine());
            }

var flag = Sort(items, (first, second) =>
            {
                comparionCount++;
                return first > second;
            });

if (flag)
                Console.WriteLine("First Parameter is bigger");
            else
                Console.WriteLine("Second Parameter is bigger");
            Console.WriteLine(string.Format("Items were compared {0} times", comparionCount));
            Console.Read();
        }

static bool Sort(int[] items, Func<int,int,bool> method)
        {
            return method(items[0], items[1]);
        }
    }

上面这一段代码闭包就出现了,函数内部读取了外部函数定义的变量。那编译器究竟干了什么呢,我们看下面的代码:

class Program
    {
        private sealed class class __LocalsDisplayClass_00000001
        {
            public int comparionCount;
            public bool __AnonymousMethd_00000000(int first,int second)
            {
                comparionCount ++;
                return first>second;
            }
        }

static void Main(string[] args)
        {
            __LocalsDisplayClass_00000001 locals =
                new __LocalsDisplayClass_00000001 ();
            locals .comparionCount =0;
            int[] items = new int[2];
            for (int i = 0; i < items.Length; i++)
            {
                Console.WriteLine("Enter a integer:");
                items[i] = int.Parse(Console.ReadLine());
            }

var flag = Sort(items,locals.__AnonymousMethd_00000000);

if (flag)
                Console.WriteLine("First Parameter is bigger");
            else
                Console.WriteLine("Second Parameter is bigger");
            Console.WriteLine(string.Format("Items were compared {0} times", comparionCount));
            Console.Read();
        }

static bool Sort(int[] items, Func<int,int,bool> method)
        {
            return method(items[0], items[1]);
        }
    }

被使用的局部变量被当作一个实例字段,包含在编译器生成的内部类中。

原文是这么说的:生成的__LocalsDisplayClass类成为闭包,他是一个数据结构(一个C#类),其中包含一个表达式以及对表达式进行求值所需的变量。

相信代码贴出来之后,就不需要过多的解释了。还有一些C#本质论的一些最佳实践和建议会陆续整理出来。再次推荐一下这本书,因为经验和基础的不同,每个人看、每次看感受和重点都会有所不同。

当然,第一篇博客,要留个纪念。感谢我的妻子对我的理解,允许我春节一个人窝在家里(没有回老家过年),有充足的时间边玩边看边学,你的支持和理解,就是我前进的动力!!!

时间: 2024-10-24 07:04:18

闭包在.NET下的实现(编译器玩的小把戏)-----C#本质论读书笔记的相关文章

IE6下png背景不透明——张鑫旭博客读书笔记

从今天开始跟着大牛张鑫旭的步伐,每天进步一点点 问题:IE6不支持png背景透明或半透明 一.可解决的方法 补充:css滤镜主要是用来实现图像的各种特殊效果.(了解) css滤镜的标识符是"filter",总体的应用上和其他的css语句相同.css滤镜可分为基本滤镜和高级滤镜两种.css滤镜分类 CSS滤镜 可以直接作用于对象上,并且立即生效的滤镜称为基本滤镜.而要配合JavaScript等脚本语言,能产生更多变幻效果的则称为高级滤镜. 只有IE可以完全的支持滤镜,Firefox支持部

基于Linux下的GCC编译器的内部预宏定义与__attribute__属性

***************************************************************************************************************************** 作者:EasyWave                                                                                    时间:2015.02.20 类别:Linux应用-GCC编

linux下的g++编译器安装

再debian下直接apt-get install gcc g++就能够了.依照类似的逻辑,再Fedora下yum install gcc g++ 报告无法找到g++包. 查了一下,原来这个包的名字叫做gcc-c++.完整的应该是yum install gcc gcc-c++. 注意安装时要先成为root用户.详细的在终端输入su之后会提示输入管理员password,输入之后在终端输入命令行yum install gcc gcc-c++就好了,依照提示一步一步的安装就能够了. linux下的g+

linux下ARM交叉编译器的安装

1.下载arm-linux-gcc编译器. 2.将下载好的编译器压缩文件拷贝到linux中,注意不要在windows中解压,因为RAR解压工具解压gzip压缩工具压缩的文件可能会出问题. 尽量将编译器的压缩文件放置到根目录下(其实也不一定是要根目录,但是我自己使用过程中发现有一个厂家提供的编译器没有安装在指定的位置,编译器提示库找不到),这样做的目的是为了尽量减少麻烦.拷贝好之后,在终端使用: # tar xvzf arm-linux-gcc-4.4.3.tar.gz 解压编译器. 3.解压好了

linux下的QQ运行玩法:pidgin-lwqq

安装pidgin: sudo apt-get install pidgin 安装pidgin-lwqq: sudo add-apt-repository ppa:lainme/pidgin-lwqq sudo apt-get update sudo apt-get install libpurple0 pidgin-lwqq 然后: 选择WebQQ选项,剩余的自己就会捣鼓了. linux下的QQ运行玩法:pidgin-lwqq,布布扣,bubuko.com

Linux下用Intel编译器编译安装NetCDF-Fortan库(4.2版本后)

本来这个问题真的没必要写的,可是真的困扰我太久%>_<%,决定还是记录一下. 首先,最权威清晰的安装文档还是官方的: Building the NetCDF-4.2 and later Fortran libraries (写此文时,最近版为4.2) 那这个文档最开始就告诉我们,自NetCDF库4.2版本以后,Fortran的库和C的库就要分开build啦!而且要装Fortran的库必须先装好C的库. 所以先装C的库咯:仍然官方文档: Getting and Building NetCDF-C

在MAC下切换GCC编译器的办法(MacPorts)

在MAC下切换GCC编译器的办法(MacPorts) 这里的办法是通过port命令选项实现的,所以需要先安装MacPorts,具体的安装步骤就不多说了,切换不同版本gcc的命令如下:执行:$ sudo port select --list gcc显示:Password:Available versions for gcc:       gcc42       llvm-gcc42       mp-gcc45 (active)       none选择llvm-gcc42作为编译器,执行:$ s

《程序员的呐喊》读书笔记(下)

接着<程序员的呐喊>读书笔记(上),继续分享下篇,这次干货比较多哦,有静动态类型的优缺点.强弱类型系统的对抗.设计模式.程序员的数学.编译器的重要性以及保守派自由派的较量,一时消化不了的建议保存以便read it later. 静态类型和动态类型的优缺点 静态类型的优点下面列出了静态类型的主要优点:(1)静态类型可以在程序运行之前,依赖其与生俱来的限制来及早发现一些类型错误.(或是在插入/更新记录,解析XML文档等情况下进行检测.)(2)静态类型有更多机会(或者说更容易)优化性能.例如只要数据

《高可用MySQL》读书笔记1 – Windows环境下压缩版MySQL安装

近日在读O'REILIY系列的<高可用MySQL>, 自然少不了主从(Master-Slave)配置和横向扩展相关的内容.Master-Slave这东西吧,在许多公司都是标配,开发中基本天天都用,遇到的问题自然也不少(如主从不同步,Master宕机),但操作权限非常有限.有些东西,只有自己看了.做了,才能真正知道原理是什么,也才能更好的去把握. 本文是高可用MySQL的第一篇读书笔记,主要记录Windows环境下压缩版MySQL(基于安装版的傻瓜式安装过程这里不再提及)的安装过程. 1. 从官