代码改变世界

Lambda表达式树

2015-03-25 15:20  FelixShen  阅读(813)  评论(1编辑  收藏  举报

我先抛出一些问题

 
要从sqlserver数据库某张表中查找某个叫 zhangsan的人的信息怎么找?
很简单 sql语句: select * from _yourTable where name='zhangsan'
 
现在同样的数据存放在xml文件里,我也要找到叫张三的怎么办?(不使用linq to xml)
也不复杂,使用xml 的XPath也可以找出来。
 
现在这些数据存放在其他类型的数据库中咋办? o(╯□╰)o 继续写可能不一样的sql语句
.....................
这样子针对同样的数据存放在不同介质中(内存,xml, 远程数据库 )有没有可能设计一种 数据结构能简明的表达这种意思,然后针对不同的存储介质在解析成对应的处理方式呢?
只要能解析成下面这样子的二叉树数据结构就可以了。
要表达这样的意思,就要用到今天的主角了 lambda 表达式树。
 
lambda 如果用作委托的参数,那他就会在底层被编译为一个匿名函数,
而如果传给C# Expression那就成了一个表达式树了,相当于是存储的描述数据结构的信息,这个信息可以被分析然后处理。
如下 
Expression<Func<string,bool>> filter=name=>name=="zhangsan";
现在 filter和委托对象就不一样了,它并不是可执行代码,如果直接filter()编译会出错,
如果现在又要想从个表达式树信息变成可执行代码怎么办?如下
 Expression< Func<string , bool >> filter = x => x == "zhangsan" ;
            var compile = filter.Compile();
            bool iszhangsan = compile("zhangsan" );
            Console.WriteLine(iszhangsan);
            Console.ReadKey();

也可以对树的结构进行解析,
Expression<Func <string, bool>> filter = x => x == "zhangsan" ;
            //我们可以对这个表达式树进行解析
            //从名称也可以看出来二叉树
            BinaryExpression body = (BinaryExpression)filter.Body;
            ParameterExpression left = (ParameterExpression)body.Left;
            ConstantExpression right = (ConstantExpression)body.Right;
            string type = body.NodeType.ToString();
    Console.WriteLine("树的左边是{0},节点符号{1},树的右边{2}" ,left,type,right);

表达式树最大的用处并不是多此一举的编译成匿名方法执行,而是 在于它可以存储 我个人认为叫做“行为信息”
将可执行代码(如C#代码) 转化成 行为数据信息(expression) 然后在转化为针对其他程序的可执行代码(如sql)
 
说的更莽撞点,就是一个符合一定规范的lambda语句,如果是表达式树的话,就会对其进行词法分析语法分析,成为一个表示行为信息的数据结构而已。
(注意并不是所有的lambda表达式都可以转化成表达式树)
 
而上述思想的一个很辉煌的实现就是 Linq,尤其是linq to sql
 
先看一下这样一个 linq 语句
 
var query = from c in db.Customers 
            where c.City == "Nantes" 
            select new { c.City, c.CompanyName };
 
linq to sql 返回的是 IQueryable类型,我们看一下IQueryable的定义
可以看到存在一个表达式树,这个表达式树就存储了上面的linq 语句想要表达的行为信息,而provider则可以显示出接下来就翻译这个行为信息成为新的可执行代码的程序,如linq to sqlserverprovider (自带) linq to oracle
等。
而之所以要进行这样由C#代码翻译成表达式树数据信息在翻译成可执行代码的原意在于。你最终是要生成sql语句串通过网络发送到远程数据库在查询出结果才可以嘛。
当然如何从上面的代码得出表达式树,又怎么分析得出合适的sql语句就不在讨论范围之内了,这里面有比较复杂的算法。
 
对于linq to objects 返回的则是IEnumerable 里面就不需要什么表达式树来存储信息了,因为本身就是针对处于同一块内存中的数据进行查询,直接逻辑查找就可以了。

就是因为 借助了强大的表达式树,linq蓬勃发展,比如linq to amazon 项目就是要将表达式树分析然后转化成webservice再执行各种操作。
 
最后总结 表达式树是为了存储行为信息,以用来跟各种程序交互,实现 不同平台不同语言之间可执行代码的转变。