string[] list = new string[] { "abc", "12", "java" };//注意字符串数组的初始化规范}
string[] yuanl = Array.FindAll(list,
delegate(string s)
{
return s.IndexOf("a") >= 0;
}
//上面等价于:string[] yuanl = list.FindAll(delegate(string s){ return s.IndexOf("a") >= 0;});
foreach (string str in yuanl)
{
Console.WriteLine(str);
}
Console.ReadLine();//这一句主要不同方式的调试环境
同样的,如果一些事件发生的时候我们只想做一点儿简单的事情的话,那么也可用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: {//定义一List对象_class
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: //筛选student数据
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: }
表达式树典型的声明形式是:
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表达式以及表达式树这两个概念。
最后总结一下:从delegate到lambda的演变
先看一个简单的delegate的例子
public static bool IsOdd(int i)
{
return (i & 1) == 1;//判断输入的数据是否为零
}
public delegate bool NumberTester(int i);//声明一委托类型
public static void PrintMatchingNumbers(int from, int to, NumberTester filter)
{
for (int i = from; i <= to; ++i)
{
if (filter(i))//判断
{
Console.WriteLine(i);//输出不为零的数据
}
}
}
调用时只需要执行:
PrintMatchingNumbers(1, 10, new NumberTester(IsOdd));//调用
等价于:PrintMatchingNumbers(1, 10,IsOdd);
很多情况下,我们只要执行只包含一个方法的代理。于是微软在.net 2.0里给我们提供了更方便的anonymous method,比如上面调用IsOdd的过程就能改写成下面这样:
PrintMatchingNumbers(1, 10, delegate(int i)
{
return (i & 1) == 1;
});
如此就避免了IsOdd的声明。微软提供匿名方法的道理是这样的:既然函数IsOdd只在一个地方被调用,那么直接在调用的地方实现岂不更省事儿。这样可以减少我们开发人员的输入。
接着到了3.0,微软更进了一步,他提出了lambda表达式。C#的lambda表达式是这样定义的:参数=> 表达式。于是,我们的代码变的更短:
PrintMatchingNumbers(1, 10, i=>(i & 1) == 1);
也就是说lambda表达式作为一个函数并当作PrintMatchingNumbers的第三个参数来执行,而这个函数只要符合delegate NumberTester的签名就行。理所当然的,lambda表达式可以附值给一个相同签名的delegate:
NumberTester a =new NumberTester();
a += i=>(i & 1) == 1;//注意这里不时数值运算而是方法挂接,a是委托对象
C# 3.0里甚至可以把声明和初始化以及赋值合到一块儿,简写成:
NumberTester a = i=>(i & 1) == 1;
如果我们把NumberTester声明为泛型的delegate,如下:
public delegate U NumberTester<T, U>(T i);
那么这个泛型的delegate应用就太广泛了,涵盖了一切有一个输入参数和一个返回值的函数,所以直接命名为Func更合适,即:
public delegate U Func<T, U>(T i);
其实上面这个类型正是在.net Framework 3.0 中最重要的命名空间 System.Core里定义的。
这样,就可以写成
Func<int,bool> a = i=>(i & 1) == 1;
下面我们再回过头来看看使用这个delegate的PrintMatchingNumbers,应该作相应的更改
public static void PrintMatchingNumbers(int from, int to, Func<int, bool> filter)
{
for (int i = from; i <= to; ++i)
{
if (filter(i))
{
Console.WriteLine(i);
}
}
}
当然,由于泛型的的引入,此处还可以进一步的把类型抽离出来以表达这个意思:在一个范围内的任意的类型的变量,以指定的方式递增,通过判断打印符合条件的值。也就可以得到算法的复用。
完整的例子:
public delegate U Func<T, U>(T i);
public delegate void Func<T>(ref T i);
static void Main(string[] args)
{
DateTime from = DateTime.Parse("2004/06/01");
DateTime to = DateTime.Parse("2005/01/18");
PrintMatchingT<DateTime>(from, to,
delegate(ref DateTime i) { i = i.AddDays(1); },
delegate(DateTime i) { return (i.Month + i.Day == 11);});
Console.WriteLine();
Console.Read();
}
public static void PrintMatchingT<T>(T from, T to, Func<T> incre, Func<T, bool> filter) where T : IComparable<T>
{
for (T i = from; (i.CompareTo(to) < 0); incre(ref i))
{
if (filter(i))
{
Console.WriteLine(i);
}
}
}
这个例子是在指定的时间范围内以天为递增量,判断代表月与日的数字加起来为11的日子。当然具体到这个例子,我们也能把bool改成泛型的,只要保证实际传入的类型能够有效的转化成bool类型的,不然filter(i)是没法放到 if判断中的。