首先,须要普及下基础知识:html
Expression咱们称之为:表达式树,数据库
而Func<>或者Action 称之为:匿名委托,Func与Action的区别是Func带返回值(至少一个参数),Action不带返回值(能够没有任何参数)。缓存
以上的关键词是在.net 3.5以后出现的,配合Linq中Lambda使用。服务器
固然Expression还能够动态的进行构造它,而不使用Lambda表达式来定义。数据结构
补充:在CSDN上找到一篇文章讲的是利用CodeDOM来生成,以为有意思,分享下:地址函数
它是一种数据结构体,用于存储须要计算、运算的一种结构。这种结构能够只是”存储“,而不进行运算。性能
就好像咱们写了一个方法函数,而不调用它同样。可是这种结构,咱们是能够在程序在运行时,进去动态改变它的。而咱们的Func一旦定义好编译后是没法更改的。学习
一般表达式树是配合委托一块儿的,好比:Expression<Func<int,int>>。this
虽然博主一直想以简单明了的说明去描述它,但感受越描越黑。。。。spa
仅仅是拿Expression来描述它的概念以为仍是不易让你们通俗易懂,因此我仍是把它跟Func<>一块儿描述吧:
首先Expression<Func<>>是能够转成Func的。反过来则不行。咱们能够理解为Func<>通过定义后,就没法改变它了。而表达式树(Expression<Func<>>则是能够进行变动的。
其次Expression<Func<>>如开头描述,仅仅只是一种数据结构,或者说载体。它自己并无运算、计算的能力。若是须要这些”能力“,则必须转为Func<>才行。
或者咱们能够这样认为:Expression<Func<>>是变量,而Func<>是”方法函数“。
二者均可以经过Lambda语法进行定义,好比:
1 Expression<Func<UserVO, object>> exp = o => o.ID > 0 && o.UserName == "farseer.net"; 2 Func<UserVO, object> fun = o => o.ID > 0 && o.UserName == "farseer.net";
可是,Func一旦定义是没法在运行时改变它的,固然我这里说的改变是动态构造,而不是从新定义(赋值)。而表达式树是能够的。
好比上面的exp变量改变成:
Expression<Func<UserVO, object>> exp = o => o.ID != 1 && (o.UserName == "steden" || o.UserName == "farseer.net")
而且,你没法在运行时知道Func的内部是什么,或者说它在作什么运算。Func的目地只是提供.net运算时的计算。
精髓:在你须要把这种表达式,转换成另一种形式时,必须使用Expression。
也能够狭义的理解为,比如Func是闭源的,你拿到别人的dll,没法改变它,只能调用它,而Expression比如你拿到了开源的代码,你随时能够更改它内部的结构。
好比:在个人Farseer.Net中,须要开发者经过传入上面的exp变量时,Farseer.Net能把它转换成SQL:
1 select * from UserVO where ID > 0 and UserName = 'farseer.net'
由于Expression是能够在运行时,分析它的数据结构。而Func是不能够的。(这里能够理解为Func是被”编译“的。)
所以,在你须要把.net的某些运算,转换成其它表达形式,传输到其它进程、服务器、文件、数据库时,必须使用表达式树。由于它是容许经过代码进行动态解析的。
解析完了上面所说的,回过头来咱们在想一想Linq To Sql 跟Linq To Object时,里面的 Where方法的形参是如何定义的:
Linq To Object:
1 public static IEnumerable<TSource> Where<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
Linq To Sql:
1 public static IQueryable<TSource> Where<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate)
因此,如今咱们知道为何IQueryable.Where 用的是:Expression<Func<TSource, bool>> predicate参数了。(由于须要转换成数据库所能识别的SQL。)
而若是咱们是本地变量(Linq To Object)时,是这样操做的:
1 var lst = new List<int>(); 2 lst.Where(o => o > 0);
注意lst.Where传入的是Func。而不是Expression,由于这里的计算直接用.net framework来完成。而不须要转换成其它语言(好比T-SQL)。
由于本系列主要讲的是表达式树,而不是委托,但你们容易把表达式树与委托搞混淆,或者不清楚他们的区别,咱们先经过这一篇来描述他们是什么,以及在什么场景下使用到。
固然,在咱们定义好一个委托时,咱们能够当即调用它:
1 Func<int, bool> fun = o => o > 0; 2 // result = true,由于传入100是大于0的 3 bool result = fun(100);
而若是是表达式树,也能够像委托那样调用,不过咱们必须经过调用Compile()转成对应的委托后才可:
1 Expression<Func<int, bool>> fun = o => o > 0; 2 // result = true,由于传入100是大于0的 3 bool result = fun.Compile()(100);
看看Compile的注释:
1 /// <summary> 2 /// 以表达式目录树的形式将强类型 lambda 表达式表示为数据结构。此类不能被继承。 3 /// </summary> 4 /// <typeparam name="TDelegate"><see cref="T:System.Linq.Expressions.Expression`1"/> 表示的委托的类型。</typeparam> 5 public sealed class Expression<TDelegate> : LambdaExpression 6 { 7 /// <summary> 8 /// 将表达式树描述的 lambda 表达式编译为可执行代码,并生成表示该 lambda 表达式的委托。 9 /// </summary> 10 /// 11 /// <returns> 12 /// 一个 <paramref name="TDelegate"/> 类型的委托,它表示由 <see cref="T:System.Linq.Expressions.Expression`1"/> 描述的已编译的 lambda 表达式。 13 /// </returns> 14 public new TDelegate Compile();
经过本篇,简单了解了表达式树与委托的区别,后面的篇幅中咱们核心讲表达式树。
其实真要完整的描述清楚表达式树及其使用,单靠几篇博文来描述是很难的。但本着分享本身的学习心得态度来跟你们分享。
表达式树对于开发者来讲是很是重要的一项技术,若是没有表达式树的出现,在之前性能、计算过程的缓存只能靠Emit来实现。(表达式树实质也是由Emity实现的。)
因此推荐全部.net开发者都须要学习并精通它。