C# Lambda表達式事例

« 博客園首頁
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判斷中的。
由此可見:混合使用泛型和匿名代理是可以寫出很精簡的代碼,當然這也往往意味着需要寫更多的註釋來說明你的意圖。
》點擊查看原文...
程序員找工作,就在博客園
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章