C# 為支持LINQ添加了許多語言特性:
了解這些新特性是全面了解LINQ的重要先解條件,因此請不要忽視它們.
(一) 隱式類型局部變量
processData這個類中的亮點是 {get;set;} 它是一個新特性, 系統會自動產生一個匿名私有變量.
1 public Int32 Id { get; set; }
2 public Int64 Memory { get; set; }
3 public string Name { get; set; }
4
5 public processData()
6 {
7
8 }
9
10 public processData(Int32 id, Int64 mem, string name)
11 {
12 Id = id;
13 Memory = mem;
14 Name = name;
15 }
var讓你無需要寫兩次變量的類型, 編譯器會自動推導,可以有效的減少代碼書寫量.
ObjectDumper.write() 是一個自定義的類, 在本系列的源代碼中會有提供, 它可以方便顯示對象的內容.
1 var process = new List<processData>();
2 foreach (var m in Process.GetProcesses())
3 {
4 var data = new processData();
5 data.Id = m.Id;
6 data.Name = m.ProcessName;
7 data.Memory = m.WorkingSet64;
8 process.Add(data);
9 }
10 ObjectDumper.Write(process);
運行結果:

(二) 對象初始化器
上面的代碼可以利用processData類的構造函數進行優化.
1 //用構造函數優化上面的代碼
2 var process = new List<processData>();
3 foreach (var m in Process.GetProcesses())
4 {
5 process.Add(new processData(m.Id, m.WorkingSet64, m.ProcessName));
6 }
7 ObjectDumper.Write(process);
最佳做法是使用對象初始化器優化, 如下:
1 //用對象初始化器優化上面的代碼
2 var process = new List<processData>();
3 foreach (var m in Process.GetProcesses())
4 {
5 process.Add(new processData()
6 {
7 Id = m.Id,
8 Memory = m.WorkingSet64,
9 Name = m.ProcessName
10 });
11 }
12 ObjectDumper.Write(process);
在你敲上面代碼時, 在{}中敲一下空格, 對象初始化器還可以支持智能感知. 相當方便.

從上面代碼可以看出,有了對象初始化器, 好處很多:
(三) Lambda表達式
要解釋Lambda表達式首先要解釋下面幾個知識:
1. 委托
C#的委托用處很大, 比如更新UI界面, 或者發揮類似函數指針的作用.
下面的例子中委托的作用類似於函數指針.
1 delegate bool match<T>(T obj);
2 private void disProcess(match<Process> fun)
3 {
4 var process = new List<processData>();
5 foreach (var m in Process.GetProcesses())
6 {
7 if (fun(m))
8 {
9 process.Add(new processData()
10 {
11 Id = m.Id,
12 Memory = m.WorkingSet64,
13 Name = m.ProcessName
14 });
15 }
16 }
17 ObjectDumper.Write(process);
18 }
19 bool filter(Process obj)
20 {
21 return obj.WorkingSet64 > 10 * 1024 * 1024;
22 }
23 private void button7_Click(object sender, EventArgs e)
24 {
25 disProcess(filter);
26 }
結果是篩選出大於10M的進程信息.

2. 匿名方法
匿名方法不用顯式定義方法名, 降低了代碼量.
下面的代碼, 相比之前的代碼來說, 就是省略了filter函數的名字, 只用了它的函數體代碼.
代碼改進部分如下:
1 //演示匿名方法
2 disProcess(delegate(Process pro)
3 {
4 return pro.WorkingSet64 > 10 * 1024 * 1024;
5 });
C#在List<T>和Array類型中添加了一些方法, 如ForEach,Find以方便使用匿名方法
下面我們修改下disProcess() 函數, 演示一下對process使用Find方法查找進程devenv.
1 private void disProcess(match<Process> fun)
2 {
3 var process = new List<processData>();
4 foreach (var m in Process.GetProcesses())
5 {
6
7 if (fun(m))
8 {
9 process.Add(new processData()
10 {
11 Id = m.Id,
12 Memory = m.WorkingSet64,
13 Name = m.ProcessName
14 });
15 }
16 }
17
18 //C#在List<T>和Array類型中添加了一些方法, 如ForEach,Find以方便使用匿名方法
19 var list1= process.Find(delegate(processData obj)
20 {
21 return obj.Name == "devenv";
22 });
23
24 ObjectDumper.Write(process);
25 }
下面我們可以直接使用Lambda表達式, 代替匿名方法.
1 //演示Lambda表達式 2 //讀作, 對傳入的Process對象,如果內存占用超過10M,則返回true 3 disProcess(s => s.WorkingSet64 > 10 * 1024 * 1024);
Lambda表達式的好處是, 無需要提供參數類型, 由編譯器從方法的簽名中自動取得.
除了實現匿名方法的功能外, Lambda還提供額外的好處:
(四) 擴展方法
下面的例子要做一件事:查找內在占用大於10M的進程,計算總內存占用,轉為MB方式表示
相對於使用普通方法,如果使用擴展方法來實現,它的語法結構易於使程序員把許多操作用.串聯起來.
這樣代碼的實際執行順序和閱讀順序就完全致.完全符合人的思維.
另一方面,擴展方法被智能感知支持,你要類型後.一下,就可以快速找到擴展方法.

1 private void button10_Click(object sender, EventArgs e)
2 {
3 //我們編寫三個擴展方法,按順序執行得到結果
4 //1. FilterOutSomeProcess() 過濾出一部分符合條件的Process
5 //2. TotalMemory() 統計這些Process的內存總占用
6 //3. BytesToMegaBytes() 把上述內存占用轉為MB方式表示
7
8 //查找內在占用大於10M的進程,計算總內存占用,轉為MB方式表示.
9 Console.WriteLine("查找內在占用大於10M的進程,計算總內存占用,轉為MB方式表示:"
10 + Environment.NewLine);
11 Console.WriteLine(
12 Process.GetProcesses()
13 .FilterOutSomeProcess(filter2)
14 .TotalMemory()
15 .BytesToMegaBytes()+"MB"
16 );
17 }
18
19 bool filter2(long mem)
20 {
21 if (mem > 10 * 1024 * 1024) return true;
22 return false;
23 }
下面是上面三個擴展方法的定義部分.
可以看見,擴展方法在要擴展類的類型前面加上關鍵字this, 其它的與普通方法沒什麼不同.
在智能感知時,擴展方法圖標有個向下的小箭頭
,以和普通方法區別開來.
1 delegate bool filterDelegate(long mem);
2 static class externFun
3 {
4 public static List<processData> FilterOutSomeProcess(this Process[] pro,filterDelegate fun)
5 {
6 var proList = new List<processData>();
7 foreach (var m in pro)
8 {
9 if(fun(m.WorkingSet64))
10 proList.Add(new processData()
11 {
12 Id = m.Id,
13 Memory = m.WorkingSet64,
14 Name = m.ProcessName
15 });
16 }
17 return proList;
18 }
19
20 public static long TotalMemory(this List<processData> data)
21 {
22 long sum=0;
23 data.ForEach(s => sum += s.Memory);
24 return sum;
25 }
26
27 public static float BytesToMegaBytes(this long sum)
28 {
29 return (float)sum / 1024f / 1024f;
30 }
31
32 }
結果如下:

LINQ也帶來一系列擴展方法,它們也可以不用於LINQ中.使用它們要包含 using System.Linq
下面我們介紹幾個:
OrderByDescending
Take
Sum
我們通過一個例子介紹上面三個擴展方法.
這個例子是想實現: 進程按內存占用排序,取前兩名耗內存大戶並計算它們耗費的總內存,用MB表示
1 private void button11_Click(object sender, EventArgs e)
2 {
3 var list1 = new List<processData>();
4 foreach (var m in Process.GetProcesses())
5 {
6 list1.Add(new processData()
7 {
8 Id = m.Id,
9 Memory = m.WorkingSet64,
10 Name = m.ProcessName
11 });
12 }
13 Console.WriteLine("進程按內存占用排序,取前兩名耗內存大戶並計算它們耗費的總內存,用MB表示"+
14 Environment.NewLine);
15 Console.WriteLine(
16 list1
17 .OrderByDescending(s => s.Memory)
18 .Take(2)
19 .Sum(s => s.Memory) / 1024f / 1024f +"MB"
20 );
21 }
關於擴展方法的幾點說明:
1. 擴展方法必須定義於靜態類,自身應該為public,static
2. 如果實例方法和擴展方法同名,先執行實例方法,擴展方法執行優先級要低.
3. 擴展方法無法訪問類型的非公有成員.
(五) 匿名類型
有了匿名類型,我們可以不再使用上面定義的processData類.而完成了同樣的事情.
1 private void button12_Click(object sender, EventArgs e)
2 {
3 var list1 = new List<object>();
4 foreach (var m in Process.GetProcesses())
5 {
6 //匿名類型可以不指定屬性的名稱, 指定屬性的名稱僅僅是為了讓代碼好懂些
7 list1.Add(
8 new
9 {
10 //Id = m.Id,
11 //Memory = m.WorkingSet64,
12 //Name = m.ProcessName
13 m.Id,
14 m.WorkingSet64,
15 m.ProcessName
16 });
17 }
18 ObjectDumper.Write(list1);
19
20
21 var obj = ReturnAGeneric(() => new
22 {
23 Time = DateTime.Now,
24 Name = "hackpig"
25 });
26 //下面的代碼是非法的, 這是因為匿名類型的實例是不可變的,它沒有set
27 //obj.Name = "lxy1";
28 //obj.Time = DateTime.Now;
29 ObjectDumper.Write(obj);
30
31 }
32
33 public static TResult ReturnAGeneric<TResult>(Func<TResult> creator)
34 {
35 return creator();
36 }
匿名類型的好處是無須特地編寫專門的類型來存放這些臨時性數據,可以大大加快程序的編寫速度.
匿名類型有一些限制,如下:
這本書前兩章的內容到此就結束了,筆者感覺講解得還是蠻精彩的.
下面放上練習的源代碼,這些代碼,有些已經不同於源書代碼,是在理解的基礎上改編的.
本文源代碼
原創文章,出自"博客園, 豬悟能'S博客" : http://www.cnblogs.com/hackpig/