zoukankan      html  css  js  c++  java
  • 什么是表达式树,它与表达式、委托有什么区别?

    序言

      首先,需要普及下基础知识:

      Expression我们称之为:表达式树,

      而Func<>或者Action 称之为:匿名委托,Func与Action的区别是Func带返回值(至少一个参数),Action不带返回值(可以没有任何参数)。

      以上的关键词是在.net 3.5之后出现的,配合Linq中Lambda使用。

      当然Expression还可以动态的进行构造它,而不使用Lambda表达式来定义。

      补充:在CSDN上找到一篇文章讲的是利用CodeDOM来生成,觉得有意思,分享下:地址

    使用场景
    1. 你需要优雅的表达式
    2. 你可能需要将某种带逻辑性的代码转成其它表现形式,比如(转成string或数据库的SQL脚本)。这会比你拼接string,显的更优雅。
    3. 你可能需要制定一个动态的规则,让客户端调用。
    什么是表达式树

      它是一种数据结构体,用于存储需要计算、运算的一种结构。这种结构可以只是”存储“,而不进行运算。

      就好像我们写了一个方法函数,而不调用它一样。但是这种结构,我们是可以在程序在运行时,进去动态改变它的。而我们的Func一旦定义好编译后是无法更改的。

      通常表达式树是配合委托一起的,比如:Expression<Func<int,int>>。

    虽然博主一直想以简单明了的说明去描述它,但感觉越描越黑。。。。

       仅仅是拿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 xx 的使用

      解析完了上面所说的,回过头来我们在想想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实现的。)

  • 相关阅读:
    win7网络共享原来如此简单,WiFi共享精灵开启半天都弱爆了!
    JQUERY UI Datepicker Demo
    Official online document, install svn server in centOS
    JAVE not work in linux
    AMR 转mp3 失败
    XD, XR, DR 股票
    Linux 下MySql 重置密码
    Difinition Of Done
    Apache, Tomcat, JK Configuration Example
    Linux 安装tomcat
  • 原文地址:https://www.cnblogs.com/kk-home/p/9249640.html
Copyright © 2011-2022 走看看