什么是m叉树_C#的λ表达式树(LambdaExpression)保姆级超详细简单入门教程

有看过我之前发表过的C#相关文章分享和阅读过我代码的朋友们可能会在我的代码里面经常看到各种各样的λ表达式动态拼接,C#的λ表达式树是一个好东西,也是别的语言学不来的,熟悉掌握λ表达式就能够实现各种linq场景的个性化操作,如动态拼接查询条件、排序方式等,也能够实现替代反射的高性能操作,比如我们常用到的IQueryable和IEnumerable,每个扩展方法就全是λ表达式树。

之前也分享过几篇关于λ表达式的文章,但鉴于还是有一部分初学者对于λ表达式树不太懂的,而网络上关于λ表达式树的科普不是很全面,今天就来填一下这个坑,分享一下λ表达式树的简单入门。

鉴于还有些伙伴不太清楚λ表达式树和委托的区别的,在教程正式开始之前,先科普下λ表达式树、lambda、委托它们之间的区别和联系吧。

λ表达式树、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它的每一个成分都是一个节点,每个节点之间进行关联,得到最终的表达式,故称之为表达式树。如图所示:

什么是m叉树_C#的λ表达式树(LambdaExpression)保姆级超详细简单入门教程_第1张图片

再比如:(a,b)=>a*3+b*4,可视化结构如下:

什么是m叉树_C#的λ表达式树(LambdaExpression)保姆级超详细简单入门教程_第2张图片

通过Expression类构造一个最基本的λ表达式树

简单介绍之后,我们就开始实战吧,我们从最简单的开始,构造上面两个基本例子:

x=>x+1

var 

一个最基本的表达式树编译完成,我们输出看一下长什么样子吧,并调用func(10)看看得到什么结果:

什么是m叉树_C#的λ表达式树(LambdaExpression)保姆级超详细简单入门教程_第3张图片

(a,b)=>a*3+b*4

看上去比上面的例子复杂了一点,但我们把它拆分成两个基本的表达式处理就好了,按照上面的图例,我们从下往上进行构建:

先组合a*3,再组合b*4,最后再将a*3和b*4进行两两组合即可完成。

var 

什么是m叉树_C#的λ表达式树(LambdaExpression)保姆级超详细简单入门教程_第4张图片

我们再来构造一个更复杂的表达式:

(a,b,c,d,e)=>((a+b)*(c-d))%e

有点蒙了吧,我们还是拆成最基本的两两组合就好了:

var 

什么是m叉树_C#的λ表达式树(LambdaExpression)保姆级超详细简单入门教程_第5张图片

感觉这玩意儿有点意思了吧!接下来继续深入研究一些基本操作。

测试类

为方便接下来的示范,我们先新建一个class:

public 

替代反射

创建实例new MyClass(100)

Expression.New操作可为class创建实例对象,比如我想通过λ表达式树实现var myClass = new MyClass(100); 其可视化结构如下:

什么是m叉树_C#的λ表达式树(LambdaExpression)保姆级超详细简单入门教程_第6张图片

代码实现:

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()();

adfa5220d70ea41a6e22be6460b305f4.png

32062aaa68f6b9262d5006b31ca84d39.png

获取属性值e=>e.MyProperty

操作对象的属性我们需要用到Expression.Property,e=>e.MyProperty表达式分析得到两部分:参数和属性,也是一种基本表达式

Expression.Parameter可得到参数e;Expression.Property可得到属性MyProperty:

var 

什么是m叉树_C#的λ表达式树(LambdaExpression)保姆级超详细简单入门教程_第7张图片

为属性赋值e=>e.List=new List(){"sdf"}

结合上面的属性操作,既然能获取属性,那就一定能设置属性值,Expression.Assign操作便是赋值操作

var 

什么是m叉树_C#的λ表达式树(LambdaExpression)保姆级超详细简单入门教程_第8张图片

调用方法e=>e.GetHashCode();

调用方法通过Expression.Call进行对象的方法调用,调用前需要先获取被调用的方法对象

var 

什么是m叉树_C#的λ表达式树(LambdaExpression)保姆级超详细简单入门教程_第9张图片

调用有参方法e=>e.SayHello("你好")

var 

什么是m叉树_C#的λ表达式树(LambdaExpression)保姆级超详细简单入门教程_第10张图片

调用Linq扩展方法e=>e.List.Contains("s")

首先我们要先获取到IEnumerable的Contains泛型扩展方法:

typeof

其次扩展方法的调用需要通过一个null实例进行调用

Expression

对于这样的链式调用,那λ表达式树的数据结构是什么样的呢?这种我们考虑从前往后构建就好了,翻译成树状结构如下:

什么是m叉树_C#的λ表达式树(LambdaExpression)保姆级超详细简单入门教程_第11张图片

所以完整代码如下:

var 

什么是m叉树_C#的λ表达式树(LambdaExpression)保姆级超详细简单入门教程_第12张图片

以上对对象的基本操作完了,我们再来一点复杂的,调用Linq扩展方法

e=>e.List.Any(item=>item.Contains("s"))

这看上去有点懵,表达式嵌套表达式,我们结合之前的方法调用和扩展方法调用,打一套组合拳就好了,表达式它也是一种参数类型嘛,我们先从里往外,再从前往后,λ表达式树的数据结构如下,先上图再写代码就不再懵:

什么是m叉树_C#的λ表达式树(LambdaExpression)保姆级超详细简单入门教程_第13张图片

所以,代码如下:

var 

对象的玩法暂时就只想到了这么多,以后想到了别的案例再随时补充。

三目表达式x=>x>60?"及格":"不及格"

这个表达式需要用到Expression.Condition,即可生成三目表达式的λ表达式树。其数据结构如下图:

什么是m叉树_C#的λ表达式树(LambdaExpression)保姆级超详细简单入门教程_第14张图片

代码实现如下:

var 

什么是m叉树_C#的λ表达式树(LambdaExpression)保姆级超详细简单入门教程_第15张图片

很简单是吧,那就又来个组合拳吧。

e => e.MyBool ? "真的" : "假的";

三目运算符+成员访问,还好,不算有难度。

var 

什么是m叉树_C#的λ表达式树(LambdaExpression)保姆级超详细简单入门教程_第16张图片

null值表达式e=>e.MyProperty??"s"

Expression.Coalesce便是对标的C#6专属的null值表达式

什么是m叉树_C#的λ表达式树(LambdaExpression)保姆级超详细简单入门教程_第17张图片
var 

什么是m叉树_C#的λ表达式树(LambdaExpression)保姆级超详细简单入门教程_第18张图片

类型转换Convert.ToInt32(x)

Expression.Convert即等价于Convert静态类,如通过Expression.Convert替代Convert.ToInt32:

var 

什么是m叉树_C#的λ表达式树(LambdaExpression)保姆级超详细简单入门教程_第19张图片

声明一个数组对象

Expression.NewArrayBounds即可生成一个创建数组对象的表达式:

var 

什么是m叉树_C#的λ表达式树(LambdaExpression)保姆级超详细简单入门教程_第20张图片

现在有了这么多的基础储备,那么接下来就来点复杂的实战吧!

实现条件表达式m=>m.MyProperty.Contains("ldqk")||m.List.Any(s=>s.Length>1&&s.Contains("a"))

我们还是先画出流程图,写代码不乱

什么是m叉树_C#的λ表达式树(LambdaExpression)保姆级超详细简单入门教程_第21张图片

写代码也没有必要按照刚才说的从内到外从前往后的顺序,也可以按局部到全部的方式来。我们一步一步便得到下面的代码:

var 

链式调用s=>s.List.Select(e => e.Length).OrderBy(x=>x).FirstOrDefault() > 1

这就比上面的这个例子简单多了,直接从左往右一步一步实现就好了

什么是m叉树_C#的λ表达式树(LambdaExpression)保姆级超详细简单入门教程_第22张图片
var 

什么是m叉树_C#的λ表达式树(LambdaExpression)保姆级超详细简单入门教程_第23张图片

λ表达式树的高级用法

由于知乎的篇幅限制,这一节内容请参考这个链接:

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.cs​github.com

通过λ表达式树自己实现一个AutoMapper:

https://github.com/ldqk/Masuit.Tools/blob/master/Masuit.Tools/Mapping/ExpressionMapper.cs​github.com

通过λ表达式树实现对象的深克隆:

https://github.com/ldqk/Masuit.Tools/blob/master/Masuit.Tools/Mapping/ExpressionCpoier.cs​github.com

将字符串转换成λ表达式的项目:

https://github.com/zzzprojects/Eval-Expression.NET​github.com

将字符串转换成动态Linq:

https://github.com/zzzprojects/System.Linq.Dynamic​github.com

动态组合条件表达式:

dbelmont/ExpressionBuilder​github.com
什么是m叉树_C#的λ表达式树(LambdaExpression)保姆级超详细简单入门教程_第24张图片

还有很多很多这样的项目和应用,这里就不再一一列举了,LinqToAnything!λ表达式树是linq的基石,linq是万能的!

最后,给大家留几个思考题吧

1. Expression.Lambda(Expression.Parameter(typeof(int), "x"), Expression.Parameter(typeof(int), "x"))这种写法有没有问题?为什么?

2. 使用λ表达式树如何自己实现一个字符串算式的计算,如输入:"5x3+4",输出15?

转自原文链接:

C#的λ表达式树(LambdaExpression)保姆级超详细简单入门教程_懒得勤快的博客_互联网分享精神​masuit.com

你可能感兴趣的:(什么是m叉树)