Linq系列(3)——Lambda 表达式,表达式树

时间:2021-03-21 18:44:47

  在开始今天的内容之前,我现在这回答杨裕欣同学的一个问题。我在昨天的文章中说过一个很核心的问题,linq其实有2套东西,用于处理本地数据源的IEnumerable<T>和处理远程数据源的IQueryable<T>。然后本地数据源可以直接代入方法的逻辑进行处理,而远程数据源必须把方法的逻辑变成表达式树,再把表达式树中相应的元素剥离出来,变成远程数据源可以识别的逻辑进行处理,远程数据源要这么做是因为没有了.net运行时这个上下文的支持,所以无法识别.net中的方法的逻辑。

      所以由此,就引出了杨裕欣同学的两个问题:

      a.比如对于IEnumerable<Customer> customers; 
       Customer类有个属性:IList<String> Addresses;
     var q = customers.Where(c => c.Addresses.Contain("xxxx"))
     是否ok
      b.如果把customers类型用AsQueryable函数转成IQueryable<Customer> 又怎么样。。。

      先回答问题a.肯定可以,因为本地数据源,有.net运行时的支持,可以直接把Contain方法的逻辑代入进行筛选

      再来回答问题b.也可以,虽然AsQueryable使处理数据源的方式变成了处理远程数据源的方式,但同样可以。为什么?ok,在回答为什么之前,我们先来看看什么情况下不行:

       所谓IQueryable这套东西无法处理的,实际就是无法变成表达式树的,根据msdn的资料:

       http://msdn.microsoft.com/zh-cn/library/bb397687.aspx

       无法变成表达式树的典型情况有两种,一种是用了.net中的方法

       () => SomeMethod()

       一种用了lamabda语句:

       Lambda 语句与 Lambda 表达式类似,只是语句括在大括号中: (input parameters) => {statement;}

   Lambda 语句的主体可以包含任意数量的语句;但是,实际上通常不会多于两个或三个语句。
   TestDelegate myDel = n => { string s = n + " " + "World"; Console.WriteLine(s); };

       总结起来,这两种情况无法变成表达式树都是因为调用了.net中的方法。所以杨裕欣同学的问题就很好理解了——Contain不是也是.net中的方法吗?为什么用了Contain方法就可以变成表达式树,调其他很多方法就不行呢?

      ok,这就引出了一个我们今天一定要讲清楚的一个概念——Provider。

      我们再来分析一下生成表达式树的这个过程,IQueryable中式靠什么把我们表达式(类似item => item.Length>3)中的基本逻辑解析成表达式树的?

      答案是——Provider,可以参考下msdn的资料:

       http://msdn.microsoft.com/zh-cn/library/bb882636.aspx

       现在问题就衍变成了为什么有的方法Provider可以解析有的却不能,我们能否找出Provider可以解析的方法的一个充要条件?

      要回答这个问题,我们先回答另一个问题:Linq是用来做什么的?Linq是用来查询关系型数据源的,这方面内容的详情请参考这篇文章http://www.cnblogs.com/Ivony/archive/2008/08/28/1278643.html

     简而言之,Linq能处理的IEnumerable<T>,sql,xml都是关系型数据源,而contain是定义在IEnumerable上的方法,相当于对关系的一种逻辑,所以Provider可以把它解析成表达式树,再直接点,当IEnumerable<T>中的数据源可以表示成一个关系时,它的方法就可以被解析成表达式树。对于关系这个概念,非常推荐大家参考上面链接的Ivony的那篇文章,非常赞。

      接下来,我们来讲今天剩下的一个重要的内容:Lambda表达式。

      我们先看一个普通的Lambda表达式:(x, y) => x == y。

  我们先来回答一个问题:Lambda表达式本质是什么?

      lambda 表达式是一种无名函数,用于计算并返回单个值。lambda 表达式可在委托类型有效的任何地方使用。

      请大家注意:只有可以表示为关系的Lambda表达式才能变成表达式树

      (x, y) => x == y 元素和元素的关系

       x => x.Length>4  元素和属性的关系

      注意:没有元素和方法的关系,要是调用了自定义的方法是无法生成合法的表达式树的。

      或者我们这么理解:构成关系的集合必须是状态的集合。

      最后再强调下:Lambda表达式就是匿名方法,能变成表达式树的Lamabda表达式是有限制的,Lambda语句无法变成表达式树。

      说下下一篇的预告:Lambda表达式树。

      话说这个系列已经写了3篇了,这个系列不是讲应用,主要想跟大家一起探讨下Linq中一些原理和理论。希望目前的这几篇文章能帮大家把以下知识点串起来:迭代器,IEnumerable,ICollection,IList之间的继承关系,扩展方法,var,匿名函数,表达式,表达式树,provider,linq中的2套东西(IEnumerable<T>和IQueryable<T>),关系。

      最后的最后链接下杨裕欣童鞋的blog:http://www.cnblogs.com/yayx/

     

 


  

相关文章