欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

【C#进阶】拥抱Lambda(一)

程序员文章站 2022-07-11 09:39:02
写在开头,好奇从这里开始(当时让加查询条件,结果竟然是一句话来发挥神奇作用): 1. 语法糖 Lambda 在我看来,=>总是一个无敌可爱的符号。嗯,包括C语言里面的 -> 这个,它总像是在说“我指到这边,你看..”。 找到了一张图[1],可以很好地说明lambda表达式,语法糖上的变化。 下面有一 ......

  写在开头,好奇从这里开始(当时让加查询条件,结果竟然是一句话来发挥神奇作用):

this.testgrade = criteriahelper.newobject<itestcase, dtotestcase>("测试等级", a => a.grade);

 

1. 语法糖 lambda

  在我看来,=>总是一个无敌可爱的符号。嗯,包括c语言里面的 -> 这个,它总像是在说“我指到这边,你看..”。

  找到了一张图[1],可以很好地说明lambda表达式,语法糖上的变化。

// 匿名方法,方法如何写应该是深入骨髓的哦:

          delegate(string text) { return text.length; }

// lambda表达式,作用与上行相同:

          (string text) => { return text.length; }

// 还可以省略括号,这看起来就更像goes to“看到这里”的感觉了:

          (string text) => text.length

// 让编译器推断参数类型:

          (text) => text.length

// 还可以省略括号:

          text => text.length

  注:lambda表达式在去掉花括号时(即仅有一句时),其实是不带;符号的,一般加上的;是赋值语句规则的。

  下面有一个例子,一个让人觉得“怎么会有传方法这种这么神奇操作”的例子:

    class film
    {
        public string name { get; set; }
        public int year { get; set; }
    }

    // main函数:
    var films = new list<film>
    {
        new film { name = "jaws", year = 1975},
        new film { name = "singing in the rain", year = 1952},
        new film { name = "some like it hot", year = 1959},
        new film { name = "the wizard of oz", year = 1939},
        new film { name = "american beauty", year = 1999}
    };

    action<film> print = film => console.writeline("name = {0}, year = {1}", film.name, film.year);
    films.foreach(print);
    // console.writeline("\r");

    films.findall(film => film.year < 1955).foreach(print);
    // console.writeline("\r");
 
    films.sort((f1,f2) => f1.name.compareto(f2.name));
    films.foreach(print);

 

2. lambda表达式与表达式树(expression tree)

  在开启本节内容前,先捋清一下:

【C#进阶】拥抱Lambda(一)

图2 [2]

  c#编译成公共中间语言(cil或者il,成果就是组件.dll文件),程序运行时再由实时编译器(just in time,jit)转化为特定于cpu的语言。

 

2.1 表达式树

  表达式树就是对象构成的树,树中每个节点本身都是一个表达式。expression类主要包括两个属性:

            type属性:表达式求值后的返回类型。

            nodetype属性:返回所代表的表达式的种类,也是节点类型。

 

  表达式树,有很多不同类型的节点。

  例如可以这样想象:一个数是一个节点,一个运算符也是一个节点,那么一个简单的2+3表达式,就是有3个节点的树形关系。

    expression firstarg = expression.constant(2);
    expression secondarg = expression.constant(3);
    expression add = expression.add(firstarg, secondarg);

    console.writeline(add); // 打印出来的是表达式 (2 + 3)

  注意:“叶”表达式总是最先创建的。

  注意:expression.constant或者是expression.add类型都是继承于expression的,这棵树的所有节点,都是expression类型。

【C#进阶】拥抱Lambda(一)

 

add

binaryexpression

nodetype = add

type = system.int32

firstarg

constantexpression

nodetype = constant

type = system.int32

secondarg

constantexpression

nodetype = constant

type = system.int32

图3 上例代码表达式树树形化表示

  继续上面的例子,既然打印出来的仅是表达式(2 + 3) ,那么能不能打印运行结果呢?

  可以想象一下,节点里面不管是数值或是运算符,其实对于表达式树来说,也就是多个节点或者少个节点的事。如果把节点当成没有实际意义的“圣诞树挂饰”(因为树上的节点“不干活”),那表达式树到底有什么意义?

  那句话是这样的,“没有lambda表达式,表达式树几乎没有任何价值”。

  我算是有些明白,“code as data”是什么意思。没有数据,代码是惘然的。但是“叶子”有了,怎么让树“活过来”?微软提供的方式是:将表达式树编译成委托——既然有各种类型的节点,怎么不能有方法类型的节点呢。

 

    expression leftarg = expression.constant(2);
    expression rightarg = expression.constant(3);
    expression add = expression.add(leftarg, rightarg);

    func<int> compiled = expression.lambda<func<int>>(add).compile();
    console.writeline(compiled());

 

【C#进阶】拥抱Lambda(一) 

  c#当中lambda表达式支持表达式树,不过还需要该节点使用compile()方法(大概就是编译成对应的func<t>类型,此方法涉及到system.reflection.methodinfo,这节内容放到后面来谈)。

 

   expression<func<int>> return5 = () => 5;   // () => 5是一个lambda表达式

   func<int> compiled = return5.compile();    // 把lambda表达式转换成表达式树。
                            // (这种转换有限制,此处不详谈。)

 

2.2 lambda表达式与linq

  linq(language integrated query)是一种基于中间查询能够直接反馈到c#语言环境当中的技术。

  linq提供器的中心思想在于:从一个熟悉的源语言(比如c#)生成一个表达式树,将结果作为一个中间格式,再将其转换成目标平台上的本地语言(比如sql)。

 

  linq to objects方式是:使用含有lambda表达式的c#查询代码,再由转化为相应的il,在clr中执行。

  linq to sql方式:编译成使用表达式树il,在执行时,动态执行sql语句(il已经处理好怎么让linq to sql提供器处理到本地语言)。

   ……

  除了linq to objects和linq to sql还有其他的数据查询方式,此处不展开。

 

 

 ==================================================================================

  下节预告:

  lambda表达式提供编译时检查的能力,它与表达式树一起的话,会有神奇反应。

 

注释:

[1]  lambda语法简写

[2] 图源自 *

[3] 此笔记主要参考 《深入理解c#》(第3版)jon skeet 著  姚琪琳 译