zoukankan      html  css  js  c++  java
  • 表达式树,它与表达式、委托

    表达式树,它与表达式、委托有什么区别?

    序言

     

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

     

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

     

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

     

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

     

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

     

    什么是表达式树

     

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

     

      就好像我们写了一个方法函数,而不调用它一样。但是这种结构,我们是可以在程序在运行时,进去动态改变它的。而我们的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实现的。)

     

      所以推荐所有.net开发者都需要学习并精通它。

     

     

     

    分类: 表达式树

  • 相关阅读:
    [敏捷软工团队博客]Beta阶段项目展示
    [敏捷软工团队博客]Beta阶段使用指南
    [敏捷软工团队博客]Beta阶段测试报告
    [敏捷软工团队博客]Beta阶段发布声明
    [Beta]the Agiles Scrum Meeting 12
    [Beta]the Agiles Scrum Meeting 11
    [Beta]the Agiles Scrum Meeting 10
    [Beta]the Agiles Scrum Meeting 9
    [Beta]the Agiles Scrum Meeting 8
    2020BUAA-个人博客-案例分析
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/4609337.html
Copyright © 2011-2022 走看看