自3.5版本以来,.NET以及微软的.NET语言开始支持表达式树。它们为这些语言的某个特定子集提供了eval形式的求值功能。考虑下面这个简单的Lambda表达式:

 Func<int, int, int> add = (x, y) => x + y;

可以知道,上述代码最后生成一个局部函数。Lambda表达式是需要编译的代码。在C#编译器运行时,把匿名函数转换为IL代码。为了建立一个表达式树,需要对上述语法稍作修改:

Expression<Func<int, int, int>> addExpr = (x, y) => x + y;

唯一的差别是保存Lambda表达式的变量的类型。这个差别指示C#编译器生成完全不同的代码。编译器不是把表达式编译成IL代码,进行加法运算,而是自动生成一段代码,这段代码可以创建复杂的对象层级结构。对象的类型全部都是从System.Linq.Expressions.Expression派生出来的。

由于保存addExpr变量中的值是Lambda表达式的一个抽象表示,因此它不可以直接执行。但是.Net Framework可以在运行时编译一个表达式树:

Func<int, int, int> addCompiled = addExpr.Compile();

C#函数式程序设计之分析表达式

把代码看成数据这个思想的主要动理论是它允许我们在运行时分析代码。有时,这个想法的动机是了解代码的执行过程,但是,它也允许我们在运行时把代码转换为最适合于当前任务的执行格式。

表达式树使用了语法树中十分常见的一种结构。层级中包含的每个元素都派生自一个公用的基类,每个类型都需要一个派生类。同时,公用基类有一个枚举类型字段,在进行任何类型转换之前,利用这个字段进行类型检查。

在实际应用中,分析表达式树的目的是把表达式树转换为不同的格式,但是它们表示同一个或同类表达式。考虑下面这个LINQ表达式:

var peopleWithInTheirName =
from p in people
where p.Name.Contains("i")
select p;

这个简短的查询要在people集合中查找所有由Name属性返回的字符串中有i的元素。对于LINQ,C#编译器把像这个例子的查询表达式转换为一系列的方法调用,把where字句转换为Lambda表达式:

p => p.Name.Contains("i");

这个表达式接受一个对象p,然后返回针对这个对象的某个条件是否成立。这种类型的表达式被称为谓词。.NET Framework专门为这些表达式预定义了一个委托类型,即Predicate<T>:

Predicate<Person> nameContainsI = p => p.Name.Contains("i");

Predicate<T>在功能上等效于Func<T , bool>:接受类型为T的一个项为参数,返回一个bool值表示这个给定的T参数是否满足某个条件。

下面是一个用递归算法求阶乘的简单函数:

static int Fact(int x)
{
int result = 1;
for (int i = 2 ; i <= x; i++)
result *= i;
return result;
}

如果使用Lambda表达式:

Func<int, int> Fact = x =>
{
int result = 1;
for (int i = 2; i <= x; i++)
result *= i;
return result;
};

表达式树的核心是运行时处理代码——不管是需要理解和分析代码,或者动态生成代码。

C#函数式程序设计之代码即数据的更多相关文章

  1. C#函数式程序设计之用闭包封装数据

    如果一个程序设计语言能够用高阶函数解决问题,则意味着数据作用域问题已十分突出.当函数可以当成参数和返回值在函数之间进行传递时,编译器利用闭包扩展变量的作用域,以保证随时能得到所需要的数据. C#函数式 ...

  2. C#函数式程序设计之泛型(下)

    C#函数式程序设计之泛型(下)   每当使用泛型类型时,可以通过where字句对泛型添加约束: + 这个例子直观地声明了一个约束:类型T必须与ListItem<string>相匹配.泛型类 ...

  3. C#函数式程序设计之函数、委托和Lambda表达式

    C#函数式程序设计之函数.委托和Lambda表达式 C#函数式程序设计之函数.委托和Lambda表达式   相信很多人都听说过函数式编程,提到函数式程序设计,脑海里涌现出来更多的是Lisp.Haske ...

  4. C#函数式程序设计之惰性列表工具——迭代器

    有效地处理数据时当今程序设计语言和框架的一个任务..NET拥有一个精心构建的集合类系统,它利用迭代器的功能实现对数据的顺序访问. 惰性枚举是一个迭代方法,其核心思想是只在需要的时候才去读取数据.这个思 ...

  5. Entity Framework 程序设计入门二 对数据进行CRUD操作和查询

    前一篇文章介绍了应用LLBL Gen生成Entity Framework所需要的类型定义,用一行代码完成数据资料的读取, <LLBL Gen + Entity Framework 程序设计入门& ...

  6. C#函数式程序设计之局部套用与部分应用

    函数式设计的核心与函数的应用以及函数如何作为算法的基本模块有关.利用局部套用技术可以把所有函数看成是函数类的成员,这些函数只有一个形参,有了局部套用,才有部分应用.部分应用是使函数模块化成为可能的两个 ...

  7. 我终于理解了LISP『代码即数据|数据即代码』的含义

    以前我一直不能理解LISP里引用的作用,感觉引用和字符串没什么区别.比如:> (define (func)     'ok) > (func) 'ok 这里把引用ok当做了函数func的返 ...

  8. C#函数式程序设计之泛型

    Intellij修改archetype Plugin配置 2014-03-16 09:26 by 破狼, 204 阅读, 0 评论,收藏, 编辑 Maven archetype plugin为我们提供 ...

  9. C#函数式程序设计之泛型(上)

    在面向对象语言中,我们可以编写一个元素为某个专用类型(可能需要为此创建一个ListElement)的List类,或者使用一个非常通用.允许添加任何类型元素的基类(在.NET中,首先想到的是System ...

随机推荐

  1. js笔记--1

    1.创建一个layer层 var GameLayer = cc.Layer.extend({ _time:null, _ship:null, _backSky:null, // 构造函数 ctor:f ...

  2. nodejs express 框架解密3-中间件模块

    本文档是基于express 3.4.6 的 在上篇中我们提到了中间件,这篇主要解释这个模块,middleware.js 为: var utils = require('./utils'); /** * ...

  3. 最近读的javascript,一些文章

    本帖子是记录一些javascript的一些文章: 1. 理解node.js 2.异步编程 http://www.ruanyifeng.com/blog/2012/12/asynchronous%EF% ...

  4. OpenSSL命令---pkcs8

    用途: pkcs8格式的私钥转换工具.它处理在PKCS#8格式中的私钥文件.它可以用多样的PKCS#5 (v1.5 and v2.0)和 PKCS#12算法来处理没有解密的PKCS#8 Private ...

  5. 【译】Python Lex Yacc手册

    本文是PLY (Python Lex-Yacc)的中文翻译版.转载请注明出处.这里有更好的阅读体验. 如果你从事编译器或解析器的开发工作,你可能对lex和yacc不会陌生,PLY是David Beaz ...

  6. Apache+PHP+Mysql 集成环境 几个软件pk

    WampServer 2.5 64位 - 工具软件 - 源码之家 2014年8月25日 - WampServer是Apache+PHP+Mysql 集成环境,拥有简单的图形和菜单安装和配置环境.支持2 ...

  7. android学习者优秀网址推荐

    非常漂亮的android UI库集合,别人整理的,如果感觉不错,赶快收藏吧!! https://github.com/wasabeef/awesome-android-ui https://githu ...

  8. Spark源码系列(八)Spark Streaming实例分析

    这一章要讲Spark Streaming,讲之前首先回顾下它的用法,具体用法请参照<Spark Streaming编程指南>. Example代码分析 val ssc = )); // 获 ...

  9. 开发常用到的terminal命令

    1.删除work_plugin目录下的.svn文件(最后面的;也是命令的一部分) sudo find /Users/maxinliang/DaTang/work_plugin ".svn&q ...

  10. Android之layout_alignBottom失效问题

    外面是一层RelativeLayout,前面的text和后面按钮都是设置centerParent_vertical,第二个hello是需要与第一个底部对齐,虽然设置alginBottom指向第一个he ...