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. Spring MVC类型转换

    类型转换器引入 为什么页面上输入"12",可以赋值给Handler方法对应的参数?这是因为框架内部帮我们做了类型转换的工作.将String转换成int 但默认类型转换器并不是可以将 ...

  2. [leetcode] 提醒整理之进制

    12. Integer to Roman Given an integer, convert it to a roman numeral. Input is guaranteed to be with ...

  3. dos学习

    >>>>>>>>>> arp-a:查看路由缓存表,所有的IP都在这里. ping <ip地址(例:192.168.x.x)>:查 ...

  4. EditText键盘弹出时,会将布局底部的导航条顶上去(解决方法之一)

    这只是其中一种方法android:windowSoftInputMode有很多属性可以添加,必须是一个state...|ajust... 我只是觉得这种比较好用 在项目的AndroidManifest ...

  5. JS简单解决并发量

    经常在写代码的时候碰到这样的场景:页面初始化时显示loading页,同时启动多个ajax并发请求获取数据,当每个ajax请求返回时结束loading. 举个例子,一个下订单的页面,要查询常用地址信息. ...

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

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

  7. tp中使用分页技术

    1 public function showList() { $m_ld = D ( 'guangxi_ld' ); $page = I ( 'get.p', 1 ); // 在配置中获取分页值 $p ...

  8. 对《分享一下自己用c++写的小地图》一文的补充

    在写完上一篇文章后,发现了一个问题: 那就是编写的插件无法实时预览. 在学习了Slate之后,我找到了方法: 重写SynchronizeProperties函数 头文件中添加: #if WITH_ED ...

  9. Yii框架 400 错误

    YII  400错误 在YII框架中400错误是csrf校验失败的意思 csrf是什么? CSRF(Cross-site request forgery跨站请求伪造,也被称为"One Cli ...

  10. Linux培训薪资过万是真事 星创客为嵌入式高端培训树标杆

    10月26日,是华清远见星创客嵌入式精英训练营首期班结业后的第15个工作日,虽然目前的学员就业成果已经超出了训练营老师们的预期,但就业工作仍然在继续进行着没有停止. 从训练营老师方面得出的统计数据,截 ...