« 博客园首页
C# Lambda表达式事例
作者:yuanl 来源:博客园 发布时间:2009-08-13 10:57 阅读:874 次 原文链接 [收藏]
C# Lambda表达式事例
Lambda表达式语法看上去真的很别扭,其实它就是便变种的匿名方法。其实你不用去管它的语法为什么是这样,你只要记住规则然后通过编写代码和比较慢慢的去体会就可以了,先来看匿名方法:
yuanl注释:首先使用集合初始化语法建立一个整型列表
List list = new List() { 1, 2, 3, 4, 5, 6, 7 };
//你也可以这样:List<int> list =new List<int>(){1, 2, 3, 4, 5, 6, 7};
//匿名方法的使用如下:
List ddNumbers = list.FindAll(
delegate(int i)
{
return (i % 2) != 0;
}

);//匿名方法到此结束,它的作用是筛选数据

foreach (var oddNumber in oddNumbers)//迭代输出数据
{
//输出奇数
Console.WriteLine(oddNumber);
}
上面的匿名方法,我们必须使用delegate来标注,而且还要保证输入参数的类型匹配,这种语法确实还是让人觉得不舒坦。Lambda表达式是如何简化FindAll()方法的:
List ddNumbers = list.FindAll(i => (i % 2) != 0);
//等价于:List ddNumbers = list.FindAll(i =>{if( (i % 2) != 0){return i;}});
解剖Lambda表达式
i => (i % 2) != 0
Lambda表达式的 => 标记(读作 goes to),它的前面是一个参数列表,后面是一个表达式或表达式块。
很明显,前面的参数列表并没有定义参数的类型(由编译器根据上下文推断出i是一个整型),所以它是隐式的。当然,我们也可以显示定义: (int i)=>(i%2)!=0);
我们这里参数列表只有一个参数,所以那个括号可以被省略。

Lambda表达式块语句形式:

List list = new List { 1,2,3,4,5,6,7};
//因为表达式需要多行代码来处理参数切记表达式块的编写规范
var testList=list.FindAll(i=>{
Console.WriteLine("调用FindAll()");
Console.WriteLine("i的值是{0}",i);
bool isOdd=((i%2)!=0);
Console.WriteLine("是否是奇数:"+isOdd);
return isOdd; //Lambda 必须返回满足某个条件的Bool值
});//注意这里的分号
foreach (var a in testList)//var换作int 同样可以(只要符合类型转换规则就好)
{
Console.WriteLine(a);
}
再看下面的例子
使用 .net 2.0 的匿名方法来搜索字符串数组中包含 a 的字符串数组
static void Main(string[] args)
{
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();//这一句主要不同方式的调试环境
}
使用 .net 3.5 的Lambda表达式来搜索字符串数组中包含 a 的字符串数组
static void Main(string[] args)
{
string[] list = new string[] { "abc", "12", "java" };

string[] ll = Array.FindAll(list, s => (s.IndexOf("a") >= 0));
foreach (string str in ll)
{
Console.WriteLine(str);
}
Console.ReadLine();
}

从上述几个例子我们可以看出:
从代码书写角度,代码可读性角度来说:Lambda表达式 比匿名方法更简单了。
而 Lambda表达式 和 匿名方法都是干的同一件事情,让我们少写一个函数定义。函数的调用和函数的实现在一起完成了。
下面有两个参数的 Lambda表达式例子:
注:别看比较复杂,LINQ中实际把 下述代码中的 delegate ,DoSomeThing 替你做了,所以你写代码的时候只需要写
var t = DoSomeThing(7, 8, (x, y) => x * y); 这么一行。
public delegate T yuanl(T t1, T t2);
class Program
{
private static T DoSomeThing(T t1,T t2, yuanl match)//注意yuanl是一T型的委托类型
{
return match(t1, t2);
}
static void Main(string[] args)
{
var t = DoSomeThing(7, 8, (x, y) => x * y);
Console.WriteLine(t);
Console.ReadLine();
}
}
下面这些写法也是对的(你只需要修改Main函数中的代码,其他地方不需要动):
var t = DoSomeThing(7, 8, (int x, int y) => x * y);
var t = DoSomeThing("7", "8", ( x, y) => x + y);//字符串拼接
或者我们写一个更复杂的: => 右边是一段语句块。
var t = DoSomeThing(7, 8, (x, y) => { if (x < 5) { return (x + 8) * y; } else { return y; } });
最前面的例子是一个参数的例子,我们就不举一个参数的例子了,下面举一个没有参数的例子:
public delegate void yuananl();
class Program
{
private static void DoSomeThing(yuananl match)
{
match();
}

static void Main(string[] args)
{
DoSomeThing(() => Console.WriteLine("jajaja"));
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判断中的。
由此可见:混合使用泛型和匿名代理是可以写出很精简的代码,当然这也往往意味着需要写更多的注释来说明你的意图。
》点击查看原文...
程序员找工作,就在博客园