博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Lambda表达式和表达式树
阅读量:5068 次
发布时间:2019-06-12

本文共 6044 字,大约阅读时间需要 20 分钟。

 lambda表达式是C#3.0中引入的比匿名方法更加简洁的一种语法,可用于创建委托或表达式树类型的匿名函数。Lambda表达式本身可划分为两种类型:语句Lambda和表达式Lambda。

表达式Lambda:

     要创建lambda表达式,则在lambda运算符 => 的左侧指定输入的参数(如果存在参数),Lambda表达式返回表达式的结果,基本形式如下:

(input parameters) => expression

只有lambda有一个参数时,括号是可以省略的,否则括号是必须的,两个及两个以上参数以逗号分隔:

(x, y) => x == y;       //参数以逗号分隔 x=>x++;()=>true;               //使用空括号指定零个输入参数

语句Lambda:

    lambda语句与lambda表达式类似,只是把语句括在大括号中,Lambda 语句的主体可以包含任意数量的语句:

(input parameters) => {statement;}

Lambda表达式传递委托:

   使用Lambda表达式使代码更为简洁,以下示例:

public delegate int DelegateTest(int n1, int n2);        static void Main(string[] args)        {            //委托:方法作为参数传递            var r1 = Result(3, 4, Sum);              //使用匿名方法传递委托            var r4 = Result(3,4,delegate(int x,int y){return x+y;});            //语句lambda传递委托            var r2 = Result(3, 4, (a, b) => { return a - b; });             //lambda传递委托            var r3 = Result(3, 4, (a, b) => a * b);             Console.ReadLine();        }        public static int Result(int a, int b, DelegateTest @delegate)        {            return @delegate(a, b);        }        public static int Sum(int a, int b)        {            return a + b;        }

上面的代码可以看出来,lambda其实和匿名方法没什么区别,使用lambda表达式创建的委托实例,不需要显示定义方法,简化了代码,所以说lambda表达式的使用使的委托实例的创建更加简洁和直观。

在编写lambda时,一般不需要为输入参数指定类型,比如(a, b) => a * b,因为编译器可以根据lambda主体、基础委托类型等因素推断类型,lambda表达式一般遵循以下几个规则:

1.lambda包含的参数数量必须与委托类型包含的参数数量一致。

2.lambda中每个输入参数必须都能够隐式转换为其对应的委托参数(逆变性)

3.lambda的返回值(如果有返回值)必须能够隐式转换为委托的返回类型(协变性)

Lambda表达式的内部机制:

    Lambda表达式并非CLR内部的固有构造,它们的实现是由C#编译器在编译时生成的。Lambda表达式为“以内嵌方式声明委托”模式提供一个对应的C#与语言构造。所以,当我们编写lambda时,编译器实际上会生成一系列代码,就以上面的代码为例,通过反编译工具查看生成的等价代码:

Main方法中使用lambda表达式传递委托,编译后生成的是匿名函数。这也证明了上面说的lambda其实就是简化了的匿名函数。

再来看看上面的匿名方法 delegate(int x,int y){return x+y;}和lambad表达式(a, b) => { return a - b; }、(a, b) => a * b),编译后实际生成了3个静态方法 <Main>b_0,<Main>b_1和<Main>b_2,也就是说,在调用的时候还是由这3个静态方法去分别实例化成委托,并作为参数传递的,又回到了最初对委托的了解:委托实现把方法作为参数传入到另一个方法。

 表达式树

     表达式树是一种允许将lambda表达式表示为树状数据结构而不是可执行逻辑的代码。

表达式树的创建:

    1.通过Lambda表达式创建表达式树

       下面的代码演示将lambda表达式表示为可执行代码和表达式树:

Func
res = x => x + 1; //Code Expression
> exp = x => x + 1; //Data

进行上面的赋值之后,委托res引用返回x+1的方法,表达式树exp引用描述表达式x=>x+1的数据结构,这是两者的明显区别。

    2.通过API创建表达式树

    要使用API创建表达式树,需要使用Expression类。该类提供创建特定类型的表达式树节点的静态方法,例如:ParameterExpression(表示一个变量或参数)或MethodCallExpression(表示一个方法调用)。下面演示使用API创建一个表达式树:

static void Main(string[] args)        {//创建表达式树:Expression
> exp = x => x + 1; ParameterExpression param = Expression.Parameter(typeof(int),"x"); ConstantExpression value = Expression.Constant(1, typeof(int)); BinaryExpression body = Expression.Add(param, value); Expression
> lambdatree = Expression.Lambda
>(body, param); Console.WriteLine("参数param:{0}", param); Console.WriteLine("描述body:{0}", body); Console.WriteLine("表达式树:{0}", lambdatree); //解析表达式树: //取得表达式树的参数 ParameterExpression dparam = lambdatree.Parameters[0] as ParameterExpression; //取得表达式树描述 BinaryExpression dbody = lambdatree.Body as BinaryExpression; //取得节点 ParameterExpression left = dbody.Left as ParameterExpression; ConstantExpression right = body.Right as ConstantExpression; Console.WriteLine("解析出的表达式:{0}=>{1} {2} {3}", param.Name, left.Name, body.NodeType, right.Value); Console.ReadLine(); }

运行结果:

   3.表达式树的修改和执行

     表达式树是不可变的,就是说如果要修改某个表达式树,必须通过复制现有的表达式并替换其中的节点,重新构造一个新的表达式树。具体可以使用ExpressionVisitor类遍历现有表达式树,并复制它的每个节点。

public class NewExpression : ExpressionVisitor    {        public ParameterExpression param { get; set; }        public NewExpression(ParameterExpression param)        {            this.param = param;        }        public Expression Replace(Expression exp)        {            return this.Visit(exp);        }        protected override Expression VisitParameter(ParameterExpression node)        {            //return base.VisitParameter(node);            return this.param;        }    }    class LambdaProgram    {static void Main(string[] args)        {            //x=>x>5 和 x=>x<10 -->  x=> x>5 && x<10            Expression
> exp1 = x => x > 5; Expression
> exp2 = x => x < 10; ParameterExpression y = Expression.Parameter(typeof(int), "y"); var newExp = new NewExpression(y); var newexp1 = newExp.Replace(exp1.Body); var newexp2 = newExp.Replace(exp2.Body); var newbody = Expression.And(newexp1, newexp2); Expression
> res = Expression.Lambda
>(newbody, y); //将表达式树描述的lambda表达式编译为可执行代码,并生成表示该lambda表达式的委托 Func
del = res.Compile(); Console.WriteLine(del(7)); Console.ReadLine(); } }

     本来以为只需要将表达式树exp1 和 exp2中的Body取出重新构建一个表达式树即可,但实际过程中遇到了问题,查了查,博客园中有这方面问题的解释:参考了一下 可以知道,exp1和exp2虽然形式相同,但是它们的“参数”不是同一个对象(此x非彼x呀),所以直接取出exp1和exp2的Body做 var newbody1 = Expression.Add(exp1.Body, exp2.Body) 操作,理想出现的是:x>5 && x<10,实际是报错的。也就是说 exp1.Body和exp2.Body并没有公用一个ParameterExpression实例。因此必须将两个表达式的参数统一成一个对象。实现方法:

public class NewExpression : ExpressionVisitor    {        public ParameterExpression param { get; set; }        public NewExpression(ParameterExpression param)        {            this.param = param;        }        public Expression Replace(Expression exp)        {            return this.Visit(exp);        }        protected override Expression VisitParameter(ParameterExpression node)        {            //return base.VisitParameter(node);            return this.param;        }    }

通过重写的VisitParameter方法,返回的是我们自定义的ParameterExpression对象,当调用Visit方法后,返回的是一个新的NewExpression对象,参数为自定义的ParameterExpression y。

      最后执行表达式树,只能执行表示lambda表达式的表达式树,表示lamdba表达式的表达式树属于LambdaExpression或Expression<TDelegate>类型。执行后可能返回一个值,也可能返回一个委托对象。

Func
del = res.Compile(); Console.WriteLine(del(7));

    关于表达式树:表达式树这一概念的引入,使得程序可以将一个lambda表达式编译成数据来表示,而不是编译成一个表示为委托的具体实现(静态方法)。利用这一特性,Linq to Sql和Linq to Xml等库能解释表达式树,并在CIL之外的上下文中使用。

 

转载于:https://www.cnblogs.com/sjqq/p/8287685.html

你可能感兴趣的文章
从.NET中委托写法的演变谈开去(上):委托与匿名方法
查看>>
小算法
查看>>
201521123024 《java程序设计》 第12周学习总结
查看>>
贪吃蛇游戏改进
查看>>
新作《ASP.NET MVC 5框架揭秘》正式出版
查看>>
在WPF中使用Caliburn.Micro搭建MEF插件化开发框架
查看>>
IdentityServer4-用EF配置Client(一)
查看>>
WPF程序加入3D模型
查看>>
WPF中实现多选ComboBox控件
查看>>
读构建之法第四章第十七章有感
查看>>
android访问链接时候报java.net.MalformedURLException: Protocol not found
查看>>
dwz ie10一直提示数据加载中
查看>>
Windows Phone开发(4):框架和页 转:http://blog.csdn.net/tcjiaan/article/details/7263146
查看>>
Unity3D研究院之打开Activity与调用JAVA代码传递参数(十八)【转】
查看>>
python asyncio 异步实现mongodb数据转xls文件
查看>>
SVN使用教程总结
查看>>
TestNG入门
查看>>
【ul开发攻略】HTML5/CSS3菜单代码 阴影+发光+圆角
查看>>
虚拟中没有eth0
查看>>
Unity 3D游戏开发学习路线(方法篇)
查看>>