阅读目录:

  1. 动态创建函数
  2. 匿名函数不足之处
  3. 理解c#中的闭包
  4. 闭包的优点

动态创建函数

大多数同学,都或多或少的使用过。回顾下c#中动态创建函数的进化:

C# 1.0中:

  public delegate string DynamicFunction(string name);
public static DynamicFunction GetDynamicFunction()
{
return GetName;
}
static string GetName(string name)
{
return name;
}
var result = GetDynamicFunction()("mushroom");

3.0写惯了是不是看起来很繁琐、落后。 刚学委托时,都把委托理解成函数指针,也来看下用函数指针实现的:

char GetName(char p);
typedef char (*DynamicFunction)(char p);
DynamicFunction GetDynamicFunction()
{
return GetName;
}
char GetName(char p)
{
return p;
};
char result = GetDynamicFunction()('m');

对比起来和c# 1.0几乎一模一样了(引用/指针差别),毕竟是同一家族的。

C# 2.0中,增加匿名函数:

      public delegate string DynamicFunction(string name);
DynamicFunction result2 = delegate(string name)
{
return name;
};

C# 3.0中,增加Lambda表达式,华丽的转身:

 public static Func<string, string> GetDynamicFunction()
{
return name => name;
}
var result = GetDynamicFunction()("mushroom");

匿名函数不足之处

虽然增加Lambda表达式,已经极大简化了我们的工作量。但确实有些不足之处:

var result = name => name;

这些写编译时是报错的。因为c#本身强类型语言的,提供var语法糖只是为了省去声明确定类型的工作量。 编译器在编译时必须能够完全推断出各参数的类型才行。代码中的name参数类型,显然在编译时无法推断出来的。

var result = (string name) => name;
Func<string, string> result2 = (string name) => name;
Expression<Func<string, string>> result3 = (string name) => name;

上面直接声明name类型呢,很遗憾这样也是报错的。代码中已经给出答案了,编译器推断不出右边表达式是属于Func<string, string>类型还是Expression<Func<string, string>>类型。

 dynamic result = name => name;
dynamic result1 = (Func<string,string>)(name => name);

用dynamic呢,同样编译器也分不出右边是个委托,我们显示转换下就可以了。

Func<string, string> function = name => name;
DynamicFunction df = function;

这里定义个func委托,虽然参数和返回值类型都和DynamicFunction委托一样,但编译时还是会报错:不能隐式转换Func<string, string>到DynamicFunction,2个类型是不兼容的。

理解c#中的闭包

谈论到动态创建函数,都要牵扯到闭包。闭包这个概念资料很多了,理论部分这里就不重复了。 来看看c#代码中闭包:

        Func<Func<int>> A = () =>
{
var age = ;
return () => //B函数
{
return age;
};
};
var result = A()();

上面就是闭包,可理解为就是: 跨作用域访问函数内变量,也有说带着数据的行为。
C#变量作用域一共有三种,即:类变量,实例变量,函数内变量。子作用域访问父作用域的变量(即函数内访问实例/类变量)在我们看来理所当然的,也符合我们一直的编程习惯。
例子中匿名函数B是可以访问上层函数A的变量age。对于编译器而言,A函数是B函数的父作用域,所以B函数访问父作用域的age变量是符合规范的。

       int age = ;
void Display()
{
Console.WriteLine(age);
int age = ;
Console.WriteLine(age);
}

上面编译会报错未声明使用,编译器检查到函数内声明age后,作用域就会覆盖父作用域的age,(像JS就undefined了)。

        Func<int> C = () =>
{
var age = ;
return age;
};

上面声明个同级函数C,那么A函数是无法访C函数中的age变量的。 简单来说就是不可跨作用域访问其他函数内的变量。 那编译器是怎么实现闭包机制的呢?

如上图,答案是升级作用域,把A函数升级为一个实例类作用域。 在编译代码期间,编译器检查到B函数使用A函数内变量时,会自动生成一个匿名类x,把原A函数内变量age提升为x类的字段(即实例变量),A函数提升为匿名类x的实例函数。下面是编译器生成的代码(精简过):

class Program1
{
static Func<Func<int>> CachedAnonymousMethodDelegate2;
static void Main(string[] args)
{
Func<Func<int>> func = new Func<Func<int>>(Program1.B);
int num = func()();
}
static Func<int> B()
{
DisplayClass cl = new DisplayClass();
cl.age = ;
return new Func<int>(cl.A);
}
}
sealed class DisplayClass
{
public int age;
public int A()
{
return this.age;
}
}

我们再来看个复杂点的例子:

    static Func<int, int> GetClosureFunction()
{
int val = ;
Func<int, int> interAdd = x => x + val;
Console.WriteLine(interAdd());
val = ;
Console.WriteLine(interAdd());
return interAdd;
}
Console.WriteLine(GetClosureFunction()());

输出结果是20、40、60。 当看到这个函数内变量val通过闭包被传递的时候,我们就知道val不仅仅是个函数内变量了。之前我们分析过编译器怎么生成的代码,知道val此时是一个匿名类的实例变量,interAdd是匿名类的实例函数。所以无论val传递多少层,它的值始终保持着,直到离开这个(链式)作用域。

关于闭包,在js当中谈论的比较多,同理,可以对比理解下:

function A() {
var age = 18;
return function () {
return age;
}
}
A()();

闭包的优点

  • 对变量的保护。想暴露一个变量值,但又怕声明类或实例变量会被其他函数污染,这时就可以设计个闭包,只能通过函数调用来使用它。
  • 逻辑连续性和变量保持。 A()是执行一部分逻辑,A()()仅接着A()逻辑继续走下去,在这个逻辑上下文期间,变量始终都被保持着,可以随意使用。

探索c#之函数创建和闭包的更多相关文章

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

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

  2. 深入理解javascript函数参数与闭包(一)

    在看此文章,希望先阅读关于函数基础内容 函数定义与函数作用域 的章节,因为这篇文章或多或少会涉及函数基础的内容,而基础内容,我放在函数定义函数作用域 章节. 本文直接赘述函数参数与闭包,若涉及相关知识 ...

  3. JavaScript函数表达式、闭包、模仿块级作用域、私有变量

    函数表达式是一种非常有用的技术,使用函数表达式可以无需对函数命名,从而实现动态编程.匿名函数,是一种强大的方式,一下总结了函数表达式的特点: 1.函数表达式不同于函数声明,函数声明要求有名字,但函数表 ...

  4. js学习之函数表达式及闭包

    来自<javascript高级程序设计 第三版:作者Nicholas C. Zakas>的学习笔记(七) 直接切入主题~ 定义函数的方式有两种: 函数声明 function functio ...

  5. JavaScript 函数作用域和闭包

    函数作用域和闭包  词法作用域   它们在定义它们的作用域里运行,而不是在执行的作用域运行,但是只有在运行时,作用域链中的属性才被 定义(调用对象),此时,可访问任何当前的绑定.   调用对象     ...

  6. JavaScript--我发现,原来你是这样的JS:函数表达式和闭包

    一.介绍 本次博客主要介绍函数表达式的内容,主要是闭包. 二.函数表达式 定义函数的两种方式:一个是函数声明,另一个就是函数表达式. //1.函数声明写法 function fn2(){ consol ...

  7. jacascript 立即执行函数(IIFE)与闭包

    前言:这是笔者学习之后自己的理解与整理.如果有错误或者疑问的地方,请大家指正,我会持续更新! 一直没搞清楚立即执行函数和闭包之间的关系,总结一下: 闭包有很多种理解:访问不到内部作用域,函数就是这样, ...

  8. Python函数二(函数名,闭包,迭代器)之杵臼之交

    函数名的使用: 函数名可以作为值,赋值给变量. 函数名可以作为参数传参给函数. 函数名可以作为返回值. 函数名可以作为元素存储在容器里. 闭包:在嵌套函数内,使用外层局部变量(非全局变量)就是一个闭包 ...

  9. javascript基础:函数参数与闭包问题

    今天在写东西的时候,对函数参数的概念有些模糊,查阅相关资料后,在博客上记点笔记,方便日后复习. 首先,在js中函数参数并没有强语言中那么要求严格,他不介意传递进来多少个参数,也不在乎传进来的参数是什么 ...

随机推荐

  1. C++ 系列:深拷贝与浅拷贝

    Copyright © 1900-2016, NORYES, All Rights Reserved. http://www.cnblogs.com/noryes/ 欢迎转载,请保留此版权声明. -- ...

  2. 基于Docker快速搭建多节点Hadoop集群--已验证

    Docker最核心的特性之一,就是能够将任何应用包括Hadoop打包到Docker镜像中.这篇教程介绍了利用Docker在单机上快速搭建多节点 Hadoop集群的详细步骤.作者在发现目前的Hadoop ...

  3. STC系列STC10F芯片解密STC10L单片机破解复制技术

    STC系列STC10F芯片解密STC10L单片机破解 STC10F12XE | STC10F12 | STC10F10XE | STC10F10解密 STC10F08XE | STC10F08 | S ...

  4. 餐厅点餐系统app总结

    总结: 三个冲刺已经结束,虽然没有说十分完美,但该实现的功能还是实现了,只是在市场是相较于专业性的缺乏竞争力,从界面到体验都需进一步优化. 每个人的进度不一样,为了同一个任务需要不断的磨合与合作,但慢 ...

  5. cassandra写数据CommitLog

    cassandra 两种方式: Cassandra-ArchitectureCommitLog Cassandra持久化-Durability 一种是配置commitlog_sync为periodic ...

  6. bing的简单英文字典工具

    今天看到园友心白水撰写的<简单翻译工具--必应字典第三方API使用方法>,感觉很不错,所以用Python也写了一个.源码如下: import urllib.request import j ...

  7. 一些比较实用的css片段

    新看了一个帖子,里面好多实用的css代码块,可拿出来当做功能库.先附上该文地址http://segmentfault.com/a/1190000002773955 里面的内容很多我挑了几个经过我验证的 ...

  8. 运行jar应用程序引用其他jar包的四种方法

    转载地址:http://www.iteye.com/topic/332580 大家都知道一个java应用项目可以打包成一个jar,当然你必须指定一个拥有main函数的main class作为你这个ja ...

  9. .net 4.0 ValidateRequest="false" 无效

    昨天安装了VisualStudio 2010 Ultimate,今天把最近的一个项目升级到了4.0下,结果跑了一下,发现关于页面启用 ValidateRequest="false" ...

  10. webform 光棒效果,删除操作弹出确定取消窗口

    鼠标移入onmouseover和鼠标移出onmouseout,代码里没大写我这也就不大写了.那首先,我们得获取Class为tr_item里的所有东西,也就是项标签里的数据.然后呢,我们定义一个oldC ...