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类包含两个属性:
- Type属性代表表达式求值后.NET类型,可把它视为一个返回类型。例如,一个表达式要获取一个字符串的长度,则该表达式的类型为int。
- 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());
后面的那些内容真心太复杂了,自己实在理解不了,而且日常使用中也没有使用过,没有底气聊这个话题,想深入的朋友可以自己深入去了解,这里就做罢了吧。
请斧正。