背景

C# 在编译器层面为我们提供了闭包机制(Java7 和 Go 也是这种思路),本文简单的做个解释。

背景知识

你必须了解:引用类型、值类型、引用、对象、值类型的值(简称值)。

关于引用、对象和值在内存的分配有如下几点规则:

  • 对象分配在堆中。
  • 作为字段的引用分配在堆中(内嵌在对象中)。
  • 作为局部变量(参数也是具备变量)的引用分配在栈中。
  • 作为字段的值分配在堆中(内嵌在对象中)。
  • 作为局部变量(参数也是具备变量)的值用分配在栈中。
  • 局部变量只能存活于所在的作用域(方法中的大括号确定了作用域的长短)。

注:按值传递和按引用传递也是需要掌握的知识点,C# 默认是按值传递的。

闭包示例

测试代码

 1         private static void Before()
2 {
3 Action[] actions = new Action[10];
4
5 for (var i = 0; i < actions.Length; i++)
6 {
7 actions[i] = () =>
8 {
9 Console.WriteLine(i);
10 };
11 }
12
13 foreach (var item in actions)
14 {
15 item();
16 }
17 }

输出结果

编译器帮我们做了是什么?

编译器帮我们生成的代码(我自己写的,可以使用 Reflector 工具自己查看)

 1         private static void After()
2 {
3 Action[] actions = new Action[10];
4
5 var anonymous = new AnonymousClass();
6
7 for (anonymous.i = 0; anonymous.i < actions.Length; anonymous.i++)
8 {
9 actions[anonymous.i ] = anonymous.Action;
10 }
11
12 foreach (var item in actions)
13 {
14 item();
15 }
16 }
17
18 class AnonymousClass
19 {
20 public int i;
21
22 public void Action()
23 {
24 Console.WriteLine(this.i);
25 }
26 }

如何修复上面的问题?

上面的例子不是我们期望的输出,让我们给出两种修改方案:

第一种(借鉴JS)

 1         private static void Fix()
2 {
3 Action[] actions = new Action[10];
4
5 for (var i = 0; i < actions.Length; i++)
6 {
7 new Action<int>((j) =>
8 {
9 actions[i] = () =>
10 {
11 Console.WriteLine(j);
12 };
13 })(i);
14 }
15
16 foreach (var item in actions)
17 {
18 item();
19 }
20 }

第二种

 1         public static void Fix2()
2 {
3 Action[] actions = new Action[10];
4
5 for (var i = 0; i < actions.Length; i++)
6 {
7 var j = i;
8
9 actions[i] = () =>
10 {
11 Console.WriteLine(j);
12 };
13 }
14
15 foreach (var item in actions)
16 {
17 item();
18 }
19 }

分析

编译器将闭包引用的局部变量转换为匿名类型的字段,导致了局部变量分配在堆中。

备注

C# 编译器帮我们做了非常多的工作,如:自动属性、类型推断、匿名类型、匿名委托、Lamda 表达式、析构方法、await 和 sync、using、对象初始化表达式、lock、默认参数 等等,这些统称为“语法糖”。

 
分类: .NET

C#编译器闭包机制的更多相关文章

  1. JavaScript——闭包机制

    闭包机制是JavaScript的重点和难点,本文希望能帮助大家轻松的学习闭包 一.什么是闭包? 闭包就是可以访问另一个函数作用域中变量的函数.下面列举出常见的闭包实现方式,以例子讲解闭包概念 func ...

  2. 以python为例讲解闭包机制

    以python为例讲解闭包机制 缘起 在学习JS的过程中,总是无可避免的接触到闭包机制,尤其是接触到react后,其函数式的编程思想更是将闭包发扬光大,作为函数式编程的重要语法结构,python自然也 ...

  3. C++编译器模板机制剖析

    思考:为什么函数模板可以和函数重载放在一块.C++编译器是如何提供函数模板机制的? 一.编译器编译原理 什么是gcc gcc(GNU C Compiler)编译器的作者是Richard Stallma ...

  4. Javascript闭包机制(转)

    原文地址:http://www.felixwoo.com/archives/247 参考:http://www.ruanyifeng.com/blog/2009/08/learning_javascr ...

  5. C#学习笔记----C#中的闭包机制

    http://www.cnblogs.com/jiejie_peng/p/3701070.html http://www.cnblogs.com/Ribbon/p/3611457.html “ 若匿名 ...

  6. JS闭包机制实现为DOM元素循环添加事件

    HTML代码: <button type='button' class='btn' id='1'>按钮1</button> <button type='button' c ...

  7. 探索c#之函数创建和闭包

    阅读目录: 动态创建函数 匿名函数不足之处 理解c#中的闭包 闭包的优点 动态创建函数 大多数同学,都或多或少的使用过.回顾下c#中动态创建函数的进化: C# 1.0中: public delegat ...

  8. C#函数式程序设计之用闭包封装数据

    如果一个程序设计语言能够用高阶函数解决问题,则意味着数据作用域问题已十分突出.当函数可以当成参数和返回值在函数之间进行传递时,编译器利用闭包扩展变量的作用域,以保证随时能得到所需要的数据. C#函数式 ...

  9. c#之函数创建和闭包

    c#之函数创建和闭包 阅读目录: 动态创建函数 匿名函数不足之处 理解c#中的闭包 闭包的优点 动态创建函数 大多数同学,都或多或少的使用过.回顾下c#中动态创建函数的进化: C# 1.0中: pub ...

随机推荐

  1. SSIS从理论到实战,再到应用(3)----SSIS包的变量,约束,常用容器

    原文:SSIS从理论到实战,再到应用(3)----SSIS包的变量,约束,常用容器 上期回顾: SSIS从理论到实战,再到应用(2)----SSIS包的控制流   首先我们来看看包里面的变量 SSIS ...

  2. 实现双8bit数据指定的位置0要么1

    <pre name="code" class="cpp"> 方法一 #include<stdio.h> #include<math ...

  3. ORACLE查看和更改的最大连接数

     第一步,在cmd命令行,进入sqlplus 第二步骤,根据提示输入username与password 1. 视图processes和sessions参数 SQL> show paramet ...

  4. form表单target的用法,实现无刷新提交页面

    form表单的target,当将iframe设置为隐藏时,可以实现当前页表单提交而不进行跳转刷新.代码如下,首页在页面里准备一个form表单和一个iframe. <form action=&qu ...

  5. ocp11g培训内部教材_052课堂笔记(042)_体系架构

    OCP 052 课堂笔记 目录 第一部分: Oracle体系架构... 4 第一章:实例与数据库... 4 1.Oracle 网络架构及应用环境... 4 2.Oracle 体系结构... 4 3. ...

  6. xdebug的安装和配置方法

    首先让php错误显示,仅仅须要改动php.ini其中的2条指令,把 displayerrors和htmlerrors都设置为On,例如以下所看到的 html_errors = On        di ...

  7. POJ1201-Intervals(差动限制)

    Intervals Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 20786   Accepted: 7866 Descri ...

  8. Oracle 11g 环境,使用utl_smtp创建一个存储过程来发送邮件

    太多的在线电子邮件存储过程.我不转发,弄个作为一个简单的例子演示. create or replace procedure Send_mail(mail_body varchar2) is smtp_ ...

  9. Linux中加入用户、删除用户时新手可能遇到的问题

    Linux中加入用户.删除用户时新手可能遇到的问题  1.创建新用户后切换到新用户:No directory, logging in with HOME=/     加入用户     #sudo us ...

  10. 通俗易懂地解决中文乱码问题(2) --- 分析解决Mysql插入移动端表情符报错 ‘incorrect string value: '\xF0...

    原文:[原创]通俗易懂地解决中文乱码问题(2) --- 分析解决Mysql插入移动端表情符报错 'incorrect string value: '\xF0... 这篇blog重点在解决问题,如果你对 ...