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. SOUI开发者论坛

    http://www.lumaba.cn/forum.php?mod=forumdisplay&fid=2 需要的朋友可以上去交流.

  2. Codeforces Round #382 Div. 2【数论】

    C. Tennis Championship(递推,斐波那契) 题意:n个人比赛,淘汰制,要求进行比赛双方的胜场数之差小于等于1.问冠军最多能打多少场比赛.题解:因为n太大,感觉是个构造.写写小数据, ...

  3. 友盟推送 .NET (C#) 服务端 SDK rest api 调用库

    友盟推送 .NET SDK rest api 介绍 该版本是基于友盟推送2.3版本封装的,网上查询了下发现没有.NET版本的调用库,官方也没有封装.NET的版本,只有python.java.php版本 ...

  4. CozyRSS开发记录-中断

    接下来会对CozyRSS做一堆的修改,包括界面和功能.因为比较杂乱,而且大多是体力活,所以就不记录了.等下次再发的时候,见到的就是一个有可用性的CozyRSS了!

  5. MR操作

    MR操作————Map.Partitioner.Shuffle.Combiners.Reduce 1.Map步骤 1.1 读取输入文件,解析成k-v对,其中每个k-v对调用一次map函数 1.2 写自 ...

  6. JDBC入门之一--连接Mysql实验

    工具:mysql-connector-java-5.1.40.eclipse 1)首先要将mysql-connector-java包整合到eclipse中,右击项目,然后选择build path,出现 ...

  7. Sublime Text 3 快捷键

    选择类Ctrl+D 选中光标所占的文本,继续操作则会选中下一个相同的文本.Alt+F3 选中文本按下快捷键,即可一次性选择全部的相同文本进行同时编辑.举个栗子:快速选中并更改所有相同的变量名.函数名等 ...

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

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

  9. 中国剩余定理(Chinese Remainder Theorem)

    我理解的中国剩余定理的含义是:给定一个数除以一系列互素的数${p_1}, \cdots ,{p_n}$的余数,那么这个数除以这组素数之积($N = {p_1} \times  \cdots  \tim ...

  10. Blend打不开wpf项目,提示无法识别的工具版本“12.0”

          讲项目文件用记事本打开然后修改为“14.0”即可