委托、匿名委托、Lambda 表达式、Expression表达式树之刨根问底

本篇不是对标题所述之概念的入门文章,重点在阐述它们的异同点和应用场景。各位看官,这里就不啰嗦了,直接上代码。

首先定义一个泛型委托类型,如下:

public delegate T Function<T>(T a, T b);

实现泛型委托的主体代码,并调用:


  public static string Add(string a, string b)
  {
    return string.Format("{0} #### {1}",a,b);
  }

 //实名委托方式
 Function<string> func = new Function<string>(Add);
 Console.WriteLine( func("hello", "world") );

 //匿名委托方式
 Function<string> func1 = new Function<string>(delegate(string a, string b) {
        return string.Format("{0} @@@@ {1}",a,b);
 });
 Console.WriteLine(func1("hello", "world"));

 //Lambda表达式方式
 Function<string> func2 = (a, b) => string.Format("{0} **** {1}", a, b);
 Console.WriteLine(func2("hello", "world"));

 Expression<Function<string>> func2_0;
 //func2_0 = func;  //不支持将委托直接赋值给表达式树
 //func2_0 = func1; //不支持将委托直接赋值给表达式树
 //func2_0 = func2; //不支持将委托直接赋值给表达式树

 //(a, b) => string.Format("{0} **** {1}", a, b)语句块的类型是lambda expression,即我们常说的lambda表达式
 //所以,func2_0 = (a, b) => string.Format("{0} **** {1}", a, b)的直接赋值是没有问题的。
 func2_0 = (a, b) => string.Format("{0} **** {1}", a, b);
 Console.WriteLine(func2_0.Compile()("hello", "world"));

以上代码展示了委托类型Function<T>主体定义的四种方式,分别是实名委托、匿名委托、Lambda表达式、expression表达式树。

从Function<T>委托主体的代码定义来看是越来越简单和友好,这些变化很大部分应归功于C#的语法糖。

总结:不管委托主体在编写的形式上怎么简化,但依然改变不了它委托类型的本质,当委托代码块被调用时会即时执行。

随着C#的发展,后来加入了expression这个东东,简称表达式树,我想用过ling to sql、linq to entity、linq to xml等等的你是不会陌生的。
expression是一种数据结构,我们可以将平常编写的C#语句块(或者叫表达式)的各部分进行分解并存入这个树结构当中,保存在expression树结构中的语句块是不能直接执行的。
当我们需要将expression结构中的数据抽取并还原时就需要调用expression.Compile()方法,这里我称之为编译。编译后得到的结果就是我们之前存入的语句块,这是数据结构还原成语句块的过程(这是一个比喻)。
当然将数据还原成语句块时依据解析引擎的不同会产生不同的输出结果,如果引擎是linq to sql那么解析后输出的就是可供数据库执行的sql,如果引擎是linq to xml则解析后输出的是Xpath之类的表达式(没亲自验证)

下面就请你和我一起来体验一下expression表达式数据的存储和编译输出吧!!!!仍以上面的场景为例子。

//expression表达式树主体构造开始
ParameterExpression paramA = Expression.Parameter(typeof(object), "a"); //声明Lambda表达式中的参数表达式a
ParameterExpression paramB = Expression.Parameter(typeof(object), "b"); //声明Lambda表达式中的参数表达式b
ConstantExpression constantExp = Expression.Constant("{0} !!!!! {1}",typeof(string));//声明文本块常量表达式
MethodCallExpression bodyExp = Expression.Call(typeof(string).GetMethod("Format", new Type[] { typeof(string), typeof(object), typeof(object) })
                , new Expression[] { constantExp, paramA, paramB }); //声明String.Format()方法调用表达式
//expression表达式树主体构造结束

//1.构造类型为LambdaExpression的lambda表达式树,编译后得到委托的基元类型(弱类型)。
LambdaExpression func3 = Expression.Lambda(bodyExp, paramA, paramB);//将以上各个表达式部分组合为Lambda表达式
Delegate dg = func3.Compile();//编译表达式树得到委托
Console.WriteLine(dg.DynamicInvoke("hello", "world"));//调用委托并将结果输出到控制台
//Console.WriteLine(func3.Compile().DynamicInvoke("hello", "world")); //上面两步可以简化为这句代码

//2.构造类型为Expression<Function<string>>的泛型lambda表达式树,编译后得到委托可直接调用。
Expression<Function<string>> func4 = Expression.Lambda<Function<string>>(bodyExp, paramA, paramB);
Console.WriteLine(func4.Compile()("xxxx", "yyyy"));

 //3.构造类型为Expression<Func<string, string, string>>的泛型lambda表达式树,编译后得到委托可直接调用。
 //与上面的区别是这里用系统定义的Func<in T1, in T2, out TResult>泛型委托代替了自定义的Function<T>委托。
 Expression<Func<string, string, string>> func5 = Expression.Lambda<Func<string, string, string>>(bodyExp, paramA, paramB);
 Console.WriteLine(func5.Compile()("yyyy", "zzzz"));

 //以上总结了expression表达式的创建和调用的不同方式,以下是几个有关expression的扩展例子
 //4.动态构造string.Concat("hello", "world")语句块
 var concatMethod = typeof(string).GetMethod("Concat", new[] { typeof(string), typeof(string) });
 var addExpr = Expression.Add(Expression.Constant("hello "), Expression.Constant("world"), concatMethod);
 Expression<Func<string>> e = Expression.Lambda<Func<string>>(addExpr);
 Console.WriteLine(e.Compile()());

 //5.动态构造Math.Sin(100)语句块
 ParameterExpression expA = Expression.Parameter(typeof(double), "a"); //参数a
 MethodCallExpression expCall = Expression.Call(
        typeof(Math).GetMethod("Sin",new Type[]{typeof(double)}),expA);
 LambdaExpression exp = Expression.Lambda(expCall, expA); // a => Math.Sin(a)
 Console.WriteLine( exp.Compile().DynamicInvoke(100) );
 //6.动态构造Console.WriteLine("aaa")语句块
 ConstantExpression _constExp = Expression.Constant("aaa", typeof(string));//一个常量
 MethodCallExpression _methodCallexp = Expression.Call(typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) }), _constExp);
 Expression<Action> consoleLambdaExp = Expression.Lambda<Action>(_methodCallexp);
 consoleLambdaExp.Compile()();

关于xx.Where(Func<T,bool> predicate)和xx.Where(Expression<Func<T, bool>> predicate)的一点看法

 //一般用在对内存对象的筛选场景下,语句被调用后即时执行并对数据进行筛选
 public static IEnumerable<string> Where(Func<string,bool> predicate)
 {
     List<string> lst = new List<string>();
     lst.Add("aaa");
     lst.Add("bbb");
     lst.Add("ccc");
     IEnumerable<string> rs = lst.Where(predicate);
     return rs;
  }

  //基于表达式树级别的条件筛选(比如linq to entity),表达式树只是sql语句逻辑关系的容器,最终的sql语句在表达式树被编译调用后才得到。
  public static IQueryable<string> Where(Expression<Func<string, bool>> predicate)
  {
      List<string> lst = new List<string>();
      lst.Add("aaa");
      lst.Add("bbb");
      lst.Add("ccc");
      IQueryable<string> rs = lst.AsQueryable().Where(predicate);
      return rs;
   }
时间: 2024-10-13 12:21:58

委托、匿名委托、Lambda 表达式、Expression表达式树之刨根问底的相关文章

委托-异步调用-泛型委托-匿名方法-Lambda表达式-事件

1. 委托 From: http://www.cnblogs.com/daxnet/archive/2008/11/08/1687014.html 类是对象的抽象,而委托则可以看成是函数的抽象.一个委托代表了具有相同参数列表和返回值的所有函数. [csharp] view plaincopy class Program { delegate int CalculateDelegate(int a, int b); int add(int a, int b) { return a + b; } s

.net 系列:Expression表达式树、lambda、匿名委托 的使用【转】

https://www.cnblogs.com/nicholashjh/p/7928205.html 首先定义一个泛型委托类型,如下: public delegate T Function<T>(T a, T b); 实现泛型委托的主体代码,并调用: 1 public static string Add(string a, string b) 2 { 3 return string.Format("{0} #### {1}",a,b); 4 } 5 //实名委托方式 6 F

C#匿名委托,匿名函数,lambda表达式

一.类型.变量.实例之间的关系. 类型>变量>实例 类型可以创建变量,实体类可以创建实例,实例可以存储在变量里. 二.委托使用过程: 1.定义委托(写好签名): 2.创建委托变量: 3.在委托变量里放函数(相同的签名). 事件:用预先定义好的委托(EventHandle)的定义的变量(接收两个参数). 窗体里自带一个委托变量:我们做的就是给这个变量追加函数. 给窗体Load事件添加一个函数 public partial class Form1 : Form { public Form1() {

个人总结:匿名委托+内置委托+Lambda表达式

匿名委托+内置委托+Lambda表达式 概要 在实际开发中,项目开发框架不同,用到的技术是不一样的,这里总结一下用Linq开发,ORM之EF开发会大量使用的Lambda表达式; 正文 之前提过.Net有一套扩展在IEumberable<T>泛型接口的扩展方法,本文是从这个基础继续总结; 1.void Action<in T>: 先拿一个ForEach()来讲: public void ForEach(Action<T> action); 这个Action<T>

委托、匿名委托和lambda表达式

1.委托 在.NET中,委托有点类似于C/C++中的函数指针,但与指针不同的是,委托是一种安全的类型,那么我们就以实现两个数的差为例,先声明一个成员方法: public int CompareTwoValue(int a, int b) { int c=a-b; return c; } 再声明一个委托: public delegate int deleMethod(int a,int b); 然后,我们可以通过这个委托调用这个上面的成员方法, 注意的一点是方法的参数类型和返回类型必须与委托的参数

委托的实现匿名函数和朗姆达表达式

**委托(delegate):一个表示方法的数据类型.间接派生于System.Delegate 委托是一个引用类型,但不必用new来实例化它.直接传递名称,而不是现实实例化.这是c#2.0开始支持的一个新语法,成为委托推断(delegate inference) 1 namespace delegateEx 2 { 3 //委托声明 4 public delegate bool ComparisonHandler(int first,int second); 5 6 public static

C++实现委托机制(三)——lambda表达式封装

C++实现委托机制(三)——lambda表达式封装1.引言:              其实原本没打算写这一章的,不过最后想了想,嗯还是把lambda表达式也一并封装进去,让这个委托也适应lambda表达式的注册.不过在之前还是需要先了解lambda表达式. 2.lambda表达式:              如果大家还有对lambda表达式不了解的可以先去了解lambda表达式的基本语法和用法.这里我们只讲跟lambda表达式封装相关的知识.我们先来看看使用lambda表达式的好处吧:1.la

Lambda表达式和表达式树

在C# 2.0中,通过方法组转换和匿名方法,使委托的实现得到了极大的简化.但是,匿名方法仍然有些臃肿,而且当代码中充满了匿名方法的时候,可读性可能就会受到影响.C# 3.0中出现的Lambda表达式在不牺牲可读性的前提下,进一步简化了委托. LINQ的基本功能就是创建操作管道,以及这些操作需要的任何状态.这些操作表示了各种关于数据的逻辑,例如数据筛选,数据排序等等.通常这些操作都是用委托来表示.Lambda表达式是对LINQ数据操作的一种符合语言习惯的表示方式. Lambda表达式不仅可以用来创

16.C#初见Lambda表达式及表达式树(九章9.1-9.3)

在说明Lambda相关知识前,我们需要了解Lambda表达式常用于LINQ,那么我们来聊下LINQ. LINQ的基本功能就是创建操作管道,以及这些操作需要的任何状态.这些操作表示了各种关于数据的逻辑:如何过滤.如何排序以及如何将不同的数据源连接在一起,等等.执行委托只是LINQ的众多能力之一.为了富有效率地使用数据库和其他查询引擎,我们需要以一种不同的方式来表示管道中的各个操作.这种不同的方式就可以使用Lambda表达式来表现.下面分别使用委托(使用匿名函数)和Lambda表达式来作出同样的事情