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; Error
24     //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());

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

  请斧正。

原文地址:https://www.cnblogs.com/a2htray/p/4209604.html