C#中引入匿名函数,多少都是受到Javascript的闭包语法和面向函数编程语言的影响。人们发现,在表达式中直接编写函数代码是一种普遍存在的需求,这种语法将比那种必须在某个特定地方定义函数的方式灵活和高效很多,比如回调和事件处理都特别适合使用表达式中直接编写函数的形式,因此C#的匿名函数也就应运而生。

初识C#中的匿名函数,多多少少并不是那么直观,在匿名函数中,可以直接使用该匿名函数所在的函数中的局部变量,这和Javascript闭包函数在语法形式和运行结果上非常相似,但两者在实现原理上却完全不同,后者是语言内在特性,而前者(C#匿名函数)只是一个编译器功能,也称语法糖。

1. Javascript闭包

作者在《Javascript本质第一篇:核心概念》《Javascript本质第二篇:执行上下文》这两篇文章中对Javascript的核心特性——包括执行上下文——做了详细介绍。很多概念都给出了明确定义,似乎缺少了闭包。

先列出一段代码,明确这里关于父子函数的定义,作为参考:

 function funParent() { // 父函数
var v = "parent funtion's variable";
function funChild() { //子函数
return v;
};
return funChild;
}
fun = funParent();

Javascript中闭包的定义:

闭包就是函数,函数就是闭包。

在作用域的角度上,将函数称为闭包。

通常在以下场景中我们更趋向于突出一个函数的闭包的概念:一个函数在其函数体中使用了定义该函数的父函数中的var变量,而且这个函数在父函数之外被使用。

例如在上面的代码的中,我们通常将函数fun叫做闭包,而不去刻意突出函数funParent的闭包的概念。

如将上面的代码改为:

 function funParent() { // 父函数
var v = "parent funtion's variable";
function funChild() { //子函数
return v;
};
funChild();
}
funParent();

这个时候的funChild函数和前面的示例代码中的funChild或fun在内在结构和行为上没有任何的区别,只是函数及其所引用的执行上下文被释放的时机的问题。

2. C#的匿名函数

C#中可以通过lambda表达式的形式在函数中定义匿名函数:

(参数) => {代码}

在匿名函数的代码中可以使用定义该匿名函数的函数中的局部变量,这一特性与Javascript中函数中的函数一样。

下面的代码在Init函数中定义两级嵌套的匿名函数:

 namespace ConsoleTest1
{
class A
{
public Action Show;
public Action<string> Set;
public Action ShowNested;
public Action<string> SetNested;
public void Init()
{
string str = "你好";
this.Show += () =>
{
Console.WriteLine(str + "!");
string name = "张三";
ShowNested = () =>
{
Console.WriteLine(str + "," + name + "!");
};
SetNested = (v) =>
{
name = v;
};
};
this.Set += (v) =>
{
str = v;
};
}
}; class Program
{
static void Main(string[] args)
{
var a = new A();
a.Init(); a.Show();
a.Set("Hello");
a.Show(); a.ShowNested();
a.SetNested("Zhang San");
a.ShowNested();
}
}
}

运行结果如下:

你好!
Hello!
Hello,张三!
Hello,Zhang San!
请按任意键继续. . .

匿名函数的运行行为与Javascript中闭包函数的运行行为相似。

但是,在C#中,name和str明显不符合局部变量的行为特性,通过反编译生成的exe文件,可以看到,Init函数已经被编译器完全重构,专门的类被创建,来封装name和str变量,实现匿名函数。匿名函数最终还是由有名函数实现。

反编译结果如下:

 namespace ConsoleTest1
{
internal class A
{
[CompilerGenerated]
private sealed class <>c__DisplayClass4
{
private sealed class <>c__DisplayClass6
{
public A.<>c__DisplayClass4 CS$<>8__locals5;
public string name;
public void <Init>b__1()
{
Console.WriteLine(this.CS$<>8__locals5.str + "," + this.name + "!");
}
public void <Init>b__2(string v)
{
this.name = v;
}
}
public string str;
public A <>4__this;
public void <Init>b__0()
{
A.<>c__DisplayClass4.<>c__DisplayClass6 <>c__DisplayClass = new A.<>c__DisplayClass4.<>c__DisplayClass6();
<>c__DisplayClass.CS$<>8__locals5 = this;
Console.WriteLine(this.str + "!");
<>c__DisplayClass.name = "张三";
this.<>4__this.ShowNested = new Action(<>c__DisplayClass.<Init>b__1);
this.<>4__this.SetNested = new Action<string>(<>c__DisplayClass.<Init>b__2);
}
public void <Init>b__3(string v)
{
this.str = v;
}
}
public Action Show;
public Action<string> Set;
public Action ShowNested;
public Action<string> SetNested;
public void Init()
{
A.<>c__DisplayClass4 <>c__DisplayClass = new A.<>c__DisplayClass4();
<>c__DisplayClass.<>4__this = this;
<>c__DisplayClass.str = "你好";
this.Show = (Action)Delegate.Combine(this.Show, new Action(<>c__DisplayClass.<Init>b__0));
this.Set = (Action<string>)Delegate.Combine(this.Set, new Action<string>(<>c__DisplayClass.<Init>b__3));
}
}
}

反编译出来的类A的定义与源代码中类A的定义已经不同,通过编译器的重构,以基本的C#语法实现了匿名函数和类似Javascript中闭包的功能。

3.结论

Javascript中所有函数本质上都是闭包,是在作用域的角度上对函数的称谓。

C#中的匿名函数行为特性上类似Javascript中闭包,通过编译器重构实现。

在Javascript中,函数是一个对象,因此函数中定义函数就是一件非常正常的事情。如果:

 function foo() {
var bar = new Function("val", "return val");
return bar("test");
};
foo();

看起来很正常,那么:

 function foo() {
function bar(val) {
return val;
};
return bar("test");
};
foo();

也是很正常的。

Javascript闭包和C#匿名函数对比分析的更多相关文章

  1. 理解javascript的闭包,原型,和匿名函数及IIFE

    理解javascript的闭包,原型,和匿名函数(自己总结) 一 .>关于闭包 理解闭包 需要的知识1.变量的作用域 例1: var n =99; //建立函数外的全局变量 function r ...

  2. Javascript 闭包与高阶函数 ( 二 )

    在上一篇 Javascript 闭包与高阶函数 ( 一 )中介绍了两个闭包的作用. 两位大佬留言指点,下来我会再研究闭包的实现原理和Javascript 函数式编程 . 今天接到头条 HR 的邮件,真 ...

  3. 闭包(Closure)和匿名函数(Anonymous function)/lambda表达式的区别

    闭包(Closure)和匿名函数(Anonymous function)/lambda表达式的区别 函数最常见的形式是具名函数(named function): function foo(){ con ...

  4. js循环函数中的匿名函数和闭包问题(匿名函数要用循环中变量的问题)

    js循环函数中的匿名函数和闭包问题(匿名函数要用循环中变量的问题) 一.总结 需要好好看下面代码 本质是因为匿名函数用到了循环中的变量,而普通方式访问的话,匿名函数的访问在循环之后,所以得到的i是循环 ...

  5. JavaScript基础---作用域,匿名函数和闭包

    匿名函数就是没有名字的函数,闭包是可访问一个函数作用域里变量的函数. 一.匿名函数 //普通函数 function box() { //函数名是 box return 'TT'; } //匿名函数 f ...

  6. JavaScript基础---作用域,匿名函数和闭包【转】

    匿名函数就是没有名字的函数,闭包是可访问一个函数作用域里变量的函数. 一.匿名函数 //普通函数 function box() { //函数名是 box return 'TT'; } //匿名函数 f ...

  7. 【转】javascript变量作用域、匿名函数及闭包

    下面这段话为摘抄,看到网上大多数人使用的是变量在使用的时候声明而不是在顶端声明,也可能考虑到js查找变量影响性能的问题,哪里用就在哪里声明,也很好. 在Javascript中,我们在写函数的时候往往需 ...

  8. Javascript 闭包与高阶函数 ( 一 )

    上个月,淡丶无欲 让我写一期关于 闭包 的随笔,其实惭愧,我对闭包也是略知一二 ,不能给出一个很好的解释,担心自己讲不出个所以然来. 所以带着学习的目的来写一写,如有错误,忘不吝赐教 . 为什么要有闭 ...

  9. javascript闭包和立即执行函数的作用

    一.闭包——closure 先看一个闭包的例子.我们想实现一个计数器,最简单的方法就是定义一个全局变量,计数的时候将其加1.但是全局变量有风险,哪里都有可能不小心改掉它.那局部变量呢, 它只在函数内部 ...

随机推荐

  1. 安装DRools开发环境

    1.下载相关安装包和开发插件 网站:http://www.jboss.org/drools/downloads.html 1.1 drools-distribution-6.3.0.Final.zip ...

  2. 《UML大战需求分析》阅读随笔(四)

    状态机图(State Machine Diagram),状态机图是通过描述某事物状态的改变来展现流程的.一般适用于流程围绕某个事物展开,例如请假的流程就围绕请假条的展开.语法,开始于结束符号,实心圆表 ...

  3. 2016-2-1 Servlet细节

    Servlet的一些细节(韩顺平老师视频讲解)(1)由于客户端是通过URL地址访问web服务器中的资源,所以Servlet程序想要被外界访问,必须把servlet程序映射到一个URL地址上.这个工作在 ...

  4. 跨界玩AR,迪奥、Hugo Boss等知名奢侈品牌将制造AR眼镜

    Snapchat因为阅后即焚消息应用而被人所熟知,前段时间这家公司拓展主要业务,未来将不再只有消息应用,还有款名为"Spectacles"的AR太阳镜.内置了一个摄像头,戴上之后即 ...

  5. spring3.0使用annotation完全代替XML(三)

    很久之前写过两篇博客: spring3.0使用annotation完全代替XML spring3.0使用annotation完全代替XML(续) 用java config来代替XML,当时还遗留下一些 ...

  6. CSS 一些知识点

  7. GitHub托管项目

    1.进入Repositories->点击 new repositories; 2.输入Repository name 直接创建项目: 3.记录你的项目地址,如:https://github.co ...

  8. 也来说说关于未在本地计算机上注册“VFPOLEDB.1”的程序的解决方法

    大家都知道VFP是一个非常古老的数据库.但是,还有一些单位用到这些数据库. 前段时间,也做了一个关于DBF数据导出的功能的测试.程序以前有同事写好了,但当我进行修改调试的时候,问题就出来了. 调试的时 ...

  9. Google Authentication的实现 - Odoo 安全登录

    在前边的一篇文章中,我们提到了利用二次验证增强Odoo登录的可靠性:http://www.cnblogs.com/kfx2007/p/6023991.html 今天我们来具体实现这一步: 后端的实现 ...

  10. *HDU 1392 计算几何

    Surround the Trees Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Other ...