首页 > 代码库 > 16.C#初见Lambda表达式及表达式树(九章9.1-9.3)

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

  在说明Lambda相关知识前,我们需要了解Lambda表达式常用于LINQ,那么我们来聊下LINQ。

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

 1 class Person 2 { 3     public DateTime BirthDay { get; set; } 4 } 5  6 public delegate int GetLifeDays(Person p);//声明一个委托类型 7 static void Main(string[] args) 8 { 9     Person p = new Person() { BirthDay = new DateTime(1900, 12, 17) };10 11     GetLifeDays gfd = delegate (Person x) { //实例化一个委托
12 return (DateTime.Now - x.BirthDay).Days;13 };14 Console.WriteLine(gfd(p));15 16 GetLifeDays gfd1 = (Person x) => { return (DateTime.Now - x.BirthDay).Days; };17 Console.WriteLine(gfd1(p));18 19 GetLifeDays gfd2 = (Person x) => (DateTime.Now - x.BirthDay).Days; //去除了后面的大括号,“;”为表达式结束,不是Lambda的结束20 Console.WriteLine(gfd2(p));21 22 GetLifeDays gfd3 = (x) => { return (DateTime.Now - x.BirthDay).Days; }; //让编译器推断参数的类型23 Console.WriteLine(gfd3(p));24 25 GetLifeDays gfd4 = (x) => (DateTime.Now - x.BirthDay).Days; //同时省去参数类型和大括号26 Console.WriteLine(gfd4(p));27 28 GetLifeDays gfd5 = x => (DateTime.Now - x.BirthDay).Days; //再进一步,省去参数列表的括号29 Console.WriteLine(gfd5(p));30 31 Console.ReadKey();32 }

   上述是单一参数的各种情况,对于有两个或多个参数的,效果是"一个到到从出生到某一天的相隔天数",某一天肯定是要大于出生那天啦。

 1 public delegate int GetDaysTo(Person p, DateTime d); 2 static void Main(string[] args) 3 { 4     Person p = new Person() { BirthDay = new DateTime(1900, 12, 17) }; 5  6     DateTime d = new DateTime(2100, 12, 12); 7     //使用匿名方法 8     GetDaysTo gdt = delegate (Person x, DateTime y) 9     {10         return (y - x.BirthDay).Days;11     };12     Console.WriteLine(gdt(p, d));13 14     GetDaysTo gdt1 = (Person x, DateTime y) => { return (y - x.BirthDay).Days; };15     Console.WriteLine(gdt1(p, d));16 17     GetDaysTo gdt2 = (Person x, DateTime y) => (y - x.BirthDay).Days;18     Console.WriteLine(gdt2(p, d));19 20     GetDaysTo gdt3 = (x, y) => (y - x.BirthDay).Days;21     Console.WriteLine(gdt3(p, d));22 23     //GetDaysTo gdt4 = x, y => (y - x.BirthDay).Days; Error24     //Console.WriteLine(gdt4(p, d));25 26     Console.ReadKey();27 }

  可以看出当参数为两个或两个以上时,不能省略参数列表中的括号,那也可以想像在语句两条或两条以上时,不能省略大括号

  下面结合之前的知识,对一个列表使用ambda表达式进行操作。

 1 //使用集合初始化器 2 List<Person> l = new List<Person> { 3     new Person { BirthDay=new DateTime(1990,11,11)}, 4     new Person { BirthDay=new DateTime(1890,12,12)}, 5     new Person { BirthDay=new DateTime(1891,12,12)}, 6     new Person { BirthDay=new DateTime(1892,12,12)}, 7     new Person() { BirthDay=new DateTime(1870,12,12)} 8 }; 9 10 //找到大于new DateTime(1890,1,1)的人11 var result0 = l.FindAll(x => x.BirthDay > new DateTime(1890, 1, 1));12 13 //按年龄从小到大排序14 l.Sort((x, y) => x.BirthDay > y.BirthDay ? -1 : 1);15 foreach (var e in l)16 {17     Console.WriteLine((DateTime.Now - e.BirthDay).Days);18 }19 20 //循环打印每个人的出生天数,效果和上面的foreach一样21 l.ForEach(x => Console.WriteLine((DateTime.Now - x.BirthDay).Days));22 23 //找到BirthDay=new DateTime(1890,12,12)的人24 var result1 = l.Find(x => x.BirthDay == new DateTime(1890, 12, 12));

  接下来,我们来说下表达式树,.NET3.5的表达式提供了一种抽象的方式将一些代码表示成一个对象树,表达式树主要用于LINQ。System.Linq.Expressions命名空间包含了代表表达式的各个类,它们都继承于Expression,一个抽象的主要包含一些静态工厂方法的类,这些方法用于创建其它表达类的实例。

  Expression类包含两个属性:

  1. Type属性代表表达式求值后.NET类型,可把它视为一个返回类型。例如,一个表达式要获取一个字符串的长度,则该表达式的类型为int。
  2. NodeType属性返回所代表的表达式的种类。它是ExpressionType枚举的成员。
1 Expression first = Expression.Constant(5);2 Expression result = Expression.Add(first, first);3 Console.WriteLine(result);

  断点对象各属性

技术分享

技术分享

  上图分别为first对象和result对象的各属性值。

  • 将表达式树编译成委托

  LambdaExpression是从Expression派生的类型之一。泛型类Expression<TDelegate>又是从LambdaExpression中派生。Expression和Expression<TDelegate>区别在于泛型类以静态类的方法标识了它是什么各类的表达式,也就是说,它确定了返回类型和参数。很显然,这是用TDelegate类型参数来表示的,它必须是一个委托类型。LambdaExpression有一个Compile方法能创建恰当类型的委托。Expression<TDelegate>也有一个同名的方法 ,但它静态类型化返回TDelegate类型的委托。如:

1 Expression first = Expression.Constant(5);2 Expression result = Expression.Add(first, first);3 Func<int> add = Expression.Lambda<Func<int>>(result).Compile();4 Console.WriteLine(add()); //10
  • 将C#Lambda表达式转换为表达式树

  Lambda表达式能显式或隐式地转换为恰当的委托实例,然而这些并非唯一能进行的转换,还可以要求编译器通过你的Lambda表达式构建一个表达式树,在执行时创建Expression<TDelegate>的一个实例。如

1 Expression<Func<int>> re = () => 5;2 Func<int> add0 = re.Compile();3 Console.WriteLine(add0());

   后面的那些内容真心太复杂了,自己实在理解不了,而且日常使用中也没有使用过,没有底气聊这个话题,想深入的朋友可以自己深入去了解,这里就做罢了吧。

  请斧正。

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