有看过我之前发表过的C#相关文章分享和阅读过我代码的朋友们可能会在我的代码里面经常看到各种各样的λ表达式动态拼接,C#的λ表达式树是一个好东西,也是别的语言学不来的,熟悉掌握λ表达式就能够实现各种linq场景的个性化操作,如动态拼接查询条件、排序方式等,也能够实现替代反射的高性能操作,比如我们常用到的IQueryable和IEnumerable,每个扩展方法就全是λ表达式树。
之前也分享过几篇关于λ表达式的文章,但鉴于还是有一部分初学者对于λ表达式树不太懂的,而网络上关于λ表达式树的科普不是很全面,今天就来填一下这个坑,分享一下λ表达式树的简单入门。
鉴于还有些伙伴不太清楚λ表达式树和委托的区别的,在教程正式开始之前,先科普下λ表达式树、lambda、委托它们之间的区别和联系吧。
1. 委托是一种类型,是方法的抽象,通过委托可以将方法以参数的形式传递给另一个方法,同时调用委托的时候,它缩包含的方法都会被实现。委托的关键字是delegate,可以自定义委托,也可以使用内置委托,通过简化,可以将Lambda表达式或Lambda语句赋值给委托,委托的调用包括同步调用和异步调用。
2. 表达式树(Expression)是一种数据结构,表达式树也称表达式目录树,是将代码以一种抽象的方式表示成一个对象树,树中每个节点本身都是一个表达式。表达式树不是可执行代码,它是一种数据结构。可以利用Lambda表达式进行声明,Lambda表达式的规则要符合Expression中Func委托的参数规则,但Lambda语句是不能声明的。
3. lambda是当委托只有一句话代码的时候的最简写形式。
4. Lambda表达式不仅可以用来创建委托实例,C#编译器也能够将他们转换成表达式树。
//Func委托,必须要有返回值,最后一个参数为返回值,前面为输入参数
λ表达式树也是代码,我们把它当成另外一种动态语言学习就好了,它也有常量、参数、运算、判断等操作,λ表达式树最终的本质就是一个方法的编译状态,如:x=>x+1这个最基本的表达式,它对应的等效方法就是:
int
我们分析x=>x+1表达式的每一个成分:
x:参数x;
=>:{}花括号;
+:add操作;
1:常量;
x=>x+1它的每一个成分都是一个节点,每个节点之间进行关联,得到最终的表达式,故称之为表达式树。如图所示:
再比如:(a,b)=>a*3+b*4,可视化结构如下:
简单介绍之后,我们就开始实战吧,我们从最简单的开始,构造上面两个基本例子:
var
一个最基本的表达式树编译完成,我们输出看一下长什么样子吧,并调用func(10)看看得到什么结果:
看上去比上面的例子复杂了一点,但我们把它拆分成两个基本的表达式处理就好了,按照上面的图例,我们从下往上进行构建:
先组合a*3,再组合b*4,最后再将a*3和b*4进行两两组合即可完成。
var
我们再来构造一个更复杂的表达式:
有点蒙了吧,我们还是拆成最基本的两两组合就好了:
var
感觉这玩意儿有点意思了吧!接下来继续深入研究一些基本操作。
为方便接下来的示范,我们先新建一个class:
public
Expression.New操作可为class创建实例对象,比如我想通过λ表达式树实现var myClass = new MyClass(100); 其可视化结构如下:
代码实现:
var newExpression = Expression.New(typeof(MyClass).GetConstructor(new[] { typeof(int) }), Expression.Constant(100)); // 有参构造函数方式
//var newExpression = Expression.New(typeof(MyClass)); //无参构造函数方式
var lambda = Expression.Lambda>(newExpression);
var myClass = lambda.Compile()();
操作对象的属性我们需要用到Expression.Property,e=>e.MyProperty表达式分析得到两部分:参数和属性,也是一种基本表达式
Expression.Parameter可得到参数e;Expression.Property可得到属性MyProperty:
var
结合上面的属性操作,既然能获取属性,那就一定能设置属性值,Expression.Assign操作便是赋值操作
var
调用方法通过Expression.Call进行对象的方法调用,调用前需要先获取被调用的方法对象
var
var
首先我们要先获取到IEnumerable的Contains泛型扩展方法:
typeof
其次扩展方法的调用需要通过一个null实例进行调用
Expression
对于这样的链式调用,那λ表达式树的数据结构是什么样的呢?这种我们考虑从前往后构建就好了,翻译成树状结构如下:
所以完整代码如下:
var
以上对对象的基本操作完了,我们再来一点复杂的,调用Linq扩展方法
这看上去有点懵,表达式嵌套表达式,我们结合之前的方法调用和扩展方法调用,打一套组合拳就好了,表达式它也是一种参数类型嘛,我们先从里往外,再从前往后,λ表达式树的数据结构如下,先上图再写代码就不再懵:
所以,代码如下:
var
对象的玩法暂时就只想到了这么多,以后想到了别的案例再随时补充。
这个表达式需要用到Expression.Condition,即可生成三目表达式的λ表达式树。其数据结构如下图:
代码实现如下:
var
很简单是吧,那就又来个组合拳吧。
三目运算符+成员访问,还好,不算有难度。
var
Expression.Coalesce便是对标的C#6专属的null值表达式
var
Expression.Convert即等价于Convert静态类,如通过Expression.Convert替代Convert.ToInt32:
var
Expression.NewArrayBounds即可生成一个创建数组对象的表达式:
var
现在有了这么多的基础储备,那么接下来就来点复杂的实战吧!
我们还是先画出流程图,写代码不乱
写代码也没有必要按照刚才说的从内到外从前往后的顺序,也可以按局部到全部的方式来。我们一步一步便得到下面的代码:
var
这就比上面的这个例子简单多了,直接从左往右一步一步实现就好了
var
由于知乎的篇幅限制,这一节内容请参考这个链接:
C#的λ表达式树(LambdaExpression)保姆级超详细简单入门教程_懒得勤快的博客_互联网分享精神masuit.com其实λ表达式树并不难,看上去很高深的东西,只有理解了其中的原理,还是很快可以上手的!毕竟在实际项目中还是很广泛应用的,掌握λ表达式树打一套组合拳,就能够实现各种各样的应用,甚至动态编译!
给EF Core增加AddOrUpdate方法:
通过Expression表达式树,为EF Core找回AddOrUpdate方法masuit.com给linq增加And和Or扩展:
https://github.com/ldqk/Masuit.Tools/blob/master/Masuit.Tools/Linq/LinqExtension.csgithub.com通过λ表达式树自己实现一个AutoMapper:
https://github.com/ldqk/Masuit.Tools/blob/master/Masuit.Tools/Mapping/ExpressionMapper.csgithub.com通过λ表达式树实现对象的深克隆:
https://github.com/ldqk/Masuit.Tools/blob/master/Masuit.Tools/Mapping/ExpressionCpoier.csgithub.com将字符串转换成λ表达式的项目:
https://github.com/zzzprojects/Eval-Expression.NETgithub.com将字符串转换成动态Linq:
https://github.com/zzzprojects/System.Linq.Dynamicgithub.com动态组合条件表达式:
dbelmont/ExpressionBuildergithub.com还有很多很多这样的项目和应用,这里就不再一一列举了,LinqToAnything!λ表达式树是linq的基石,linq是万能的!
1. Expression.Lambda(Expression.Parameter(typeof(int), "x"), Expression.Parameter(typeof(int), "x"))这种写法有没有问题?为什么?
2. 使用λ表达式树如何自己实现一个字符串算式的计算,如输入:"5x3+4",输出15?
转自原文链接:
C#的λ表达式树(LambdaExpression)保姆级超详细简单入门教程_懒得勤快的博客_互联网分享精神masuit.com