4. Lambda Expressions (Lambda表达式)与Expressions Tree(表达式树)

时间:2021-06-25 18:50:05

【返回目录】

什么是Lambda表达式?我们可以认为它是匿名方法的简洁写法,例如下面这个匿名方法:

   1: delegate(int age)
   2: {
   3:     return age < 18;
   4: }

我们就可以写成Lambda表达式的形式:

   1: age => age < 18

这是一个很简单的例子,基本上一目了然。Lambda的基本形式是argument-list => expression,=>是Lambda表达式的标识符而不是运算符,显而易见,=>的作用就是{}花括号的作用,与此同时,我们看到了在Lambda表达式中被省略掉的还有参数声明中的数据类型和return关键字以及句尾的分号,因为它们在Lambda表达式中不是必需的,在有参数的情况下表达式总是会返回一个值,而没有参数的情况下表达式也可以不必返回任何值,参数的数据类型是靠编译器从右边的表达式中反推出来的,另外,=>右边只是一个表达式而不是一句完整的语句,所以如果画蛇添足地加上分号就违背了语法规则。当然,右边的表达式中也是可以包含多条语句的,这时候语句之间依然采用分号来分割,但前提是这些语句都要被放置在一对{}花括号里面,不过我的建议还是在语句较少功能较简单的时候才只用Lambda表达式,否则写出来的代码在可读性方面可能会比较差,为以后的维护和修改工作带来不必要的麻烦,要像对待内联函数一样对待Lmbda表达式。

在单个参数的Lambda表达式中我们可以省略参数列表部分的小括号,但是多个参数或者没有参数以及需要显式地为参数设置数据类型的情况下,小括号就不能省略了,例如:

   1: // No parameter.
   2: () => Console.WriteLine("Hello, world!")
   3:  
   4: // Multiple parameters.
   5: (m, n) => m = n * 10
   6:  
   7: // Explicitly typed declaration.
   8: (int age) => age < 18

那么我们什么时候用Lambda表达式呢?很简单,前面说过我们可以认为Lambda表达式就是匿名方法的简洁写法,所以原先采用匿名方法的地方现在我们就可以替换成Lambda表达式了。

同样的,如果一些事件发生的时候我们只想做一点儿简单的事情的话,那么也可用Lambda表达式来实现:

   1: System.Windows.Forms.Timer timer = new System.Windows.Forms.Timer();
   2: timer.Interval = 2000;
   3: timer.Tick += (sender, e) => { timer.Stop(); MessageBox.Show("Hello, Lambda Expression!"); };

最后我们来看一段比较完整的代码并以此结束对Lambda表达式的介绍吧:

   1: using System;
   2: using System.Collections.Generic;
   3:  
   4: namespace LambdaExpressionDemo
   5: {
   6:     class Program
   7:     {
   8:         private static List<StudentData> _class = null;
   9:  
  10:         static void Initialization()
  11:         {
  12:             _class = new List<StudentData>();
  13:  
  14:             _class.Add(new StudentData("ZeroCool", 24, GenderType.Male, 87));
  15:             _class.Add(new StudentData("Michael", 24, GenderType.Male, 93));
  16:             _class.Add(new StudentData("Frieda", 22, GenderType.Female, 98));
  17:             _class.Add(new StudentData("Somebody", 23, GenderType.Male, 81));
  18:         }
  19:  
  20:         static void Main(string[] args)
  21:         {
  22:             Initialization();
  23:  
  24:             if (_class == null || _class.Count == 0)
  25:             {
  26:                 throw new InvalidOperationException("The system initialization was failed.");
  27:             }
  28:  
  29:             StudentData studentData = _class.Find(student => student.MathScore >= 90 && student.MathScore < 95);
  30:             RepresentData(studentData);
  31:  
  32:             studentData = _class.Find(student => student.Name.Equals("ZeroCool", StringComparison.InvariantCultureIgnoreCase));
  33:             RepresentData(studentData);
  34:  
  35:             studentData = _class.Find(student => student.Age < 23);
  36:             RepresentData(studentData);
  37:  
  38:             Console.ReadLine();
  39:         }
  40:  
  41:         static void RepresentData(StudentData student)
  42:         {
  43:             if (student == null)
  44:             {
  45:                 Console.WriteLine("No mached student.");
  46:  
  47:                 return;
  48:             }
  49:  
  50:             Console.WriteLine("Name:\t\t" + student.Name);
  51:             Console.WriteLine("Age:\t\t" + student.Age);
  52:             Console.WriteLine("Gender:\t\t" + student.Gender.ToString());
  53:             Console.WriteLine("Math Score:\t" + student.MathScore);
  54:             Console.WriteLine();
  55:         }
  56:     }
  57:  
  58:     public enum GenderType
  59:     {
  60:         Male = 0,
  61:         Female
  62:     }
  63:  
  64:     public class StudentData
  65:     {
  66:         private string _name = String.Empty;
  67:         public string Name
  68:         {
  69:             get { return this._name; }
  70:             set { this._name = value; }
  71:         }
  72:  
  73:         private int _age = 0;
  74:         public int Age
  75:         {
  76:             get { return this._age; }
  77:             set { this._age = value; }
  78:         }
  79:  
  80:         private GenderType _gender;
  81:         public GenderType Gender
  82:         {
  83:             get { return this._gender; }
  84:             set { this._gender = value; }
  85:         }
  86:  
  87:         private int _mathScore = 0;
  88:         public int MathScore
  89:         {
  90:             get { return this._mathScore; }
  91:             set { this._mathScore = value; }
  92:         }
  93:  
  94:         public StudentData()
  95:         {
  96:         }
  97:  
  98:         public StudentData(string name, int age, GenderType gender, int mathScore)
  99:         {
 100:             this._name = name;
 101:             this._age = age;
 102:             this._gender = gender;
 103:             this._mathScore = mathScore;
 104:         }
 105:  
 106:         public delegate void EmptyDelegate();
 107:  
 108:         EmptyDelegate dl = () => Console.WriteLine();
 109:     }
 110: }

上面的代码肯定还有不够完善的地方,肯定还可以被再次提炼、加工,那么就留给各位网友仁者见仁,智者见智吧。好了,Lambda表达式大概就介绍到这里,接下来我们一起来探讨一下表达式树吧。

表达式树典型的声明形式是:

   1: Func<int, int> func = input => input * input;
   2: Expression<Func<int, int>> expression = input => input * input;

我们必须要使用的类型是System.Linq.Expressions命名空间中的Expression<T>,而这个T是定义表达式签名的委托的类型,这种把一个Lambda表达式看做一个数据结构而嵌套在另一个Lambda表达式中的做法使得现在的expression不是一个委托而是表达式树的数据结构了。接下来的问题是我们应该如何使用这个表达式树呢?请看完整的代码:

   1: static void Main(string[] args)
   2: {
   3:     Func<int, int> func = input => input * input;
   4:     Console.WriteLine(func(3).ToString());
   5:  
   6:     Expression<Func<int, int>> expression = input => input * input;
   7:     Func<int, int> fun = expression.Compile();
   8:     Console.WriteLine(fun(5).ToString());
   9:  
  10:     Console.ReadLine();
  11: }

正因为func是可执行代码而expression是数据,所以在使用方式上也会有所差别的,希望以上这些代码可以很好地让你理解Lambda表达式以及表达式树这两个概念。

好了,今天我们打之探讨了一下Lambda表达式以及表达式树,希望能够激起大家深入研究学习的热情,下一次我们主要来探讨一下Object and Collection Initializers(对象和集合初始器),欲知后事如何,且听下回分解!