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. Unity引擎IOS执行档大小优化

    简介 苹果对于IOS执行档的大小是有明确的限制的,其中TEXT段的大小不能超过80M,否则提审将会被苹果拒绝,同时,如果TEXT段过于太大,那么在苹果进行加密之后,很容易出现解压失败等各种异常,最终导 ...

  2. jquery的animate({})动画整理

    在网页制作的过程中少不了用到各种动画,形式多种多样,flash,css,js,canvas,等等都能实现,对于其优劣和效果只能说各有千秋. 什么是动画效果,其实网页中的渐变效果就是一种很基础的动画,动 ...

  3. android开发:深入理解View(一):从setContentView谈起

    我们都知道 MVC,在Android中,这个 V 即指View,那我们今天就来探探View的究竟. 在onCreate方法中,可以调用this.setContentView(layout_id),来设 ...

  4. 转:MYSQL连接字符串参数解析(解释)

    被迫转到MySQL数据库,发现读取数据库时,tinyint类型的值都被转化为boolean了,这样大于1的值都丢失,变成true了.查阅资料MySQL中无Boolean类型,都是存储为tinyint了 ...

  5. React 编程思想翻译及学习笔记

    第一步:把UI图按组件层次结构拆分开 FilterableProductTable (橙色): 包含整个例子 SearchBar (蓝色): 接收所有用户输入 ProductTable (绿色): 基 ...

  6. solr 添加索引

    添加索引模板: <add> <doc> <field name="employeeId">05991</field> <fie ...

  7. 单片机与控制实验(2)——LED点阵显示屏

    一.实验目的和要求 了解LED点阵显示的基本原理和实现方法.掌握点阵汉字库的编码和从标准字库中提取汉字编码的方法. 二.实验设备 单片机测控实验系统 LED点阵显示器实验模块 Keil开发环境 STC ...

  8. java-并发-同步

    浏览以下内容前,请点击并阅读 声明 线程间的通信主要是通过访问以及对象引用字段,这种形式的通信非常高效,但是会产生两种可能的错误:线程干扰和内存一致性错误,反正这些错误的工具就是同步. 然而,同步可能 ...

  9. WPF CodeBehind后台动态创建图片及添加事件

    问题:WPF中DataGrid需要动态生成列并绑定值,首列包含图片和文本,点击图片触发事件. 难点:1.图片资源在VisualTree中的绑定   2.图片的事件绑定 public class Mai ...

  10. git: 常用功能等

    1. an very useful simple git guide link: http://rogerdudler.github.io/git-guide/index.zh.html