iT邦幫忙

DAY 7
1

分享一些學習心得系列 第 7

LINQ自學筆記-打地基-匿名委派

  • 分享至 

  • xImage
  •  

匿名委派大幅減少使用委派所需要撰寫的程式碼數量,而且實用價值更高,若沒有匿名委派,LINQ 根本無法應用在真實環境中,因為程式碼太冗長、繁雜了。本篇將延續昨天具名委派範例,讓大家有深刻的體會。
自學筆記這系列是我自己學習的一些心得分享,歡迎指教。這系列的分享,會以 C# + 我比較熟的 Net 3.5 環境為主。
另外本系列預計至少會切成【打地基】和【語法應用】兩大部分做分享。打地基的部分,講的是 LINQ 的組成元素,這部分幾乎和 LINQ 無關,反而是 C# 2.0、C# 3.0 的一堆語言特性,例如:型別推斷、擴充方法、泛型、委派等等,不過都會把分享的範圍限制在和 LINQ 應用有直接相關功能。
PS. LINQ 自學筆記幾乎所有範例,都可直接複製到 LINQPad 4 上執行(大多是用 Statements 和 Program 模式)。因為它輕巧好用,功能強大,寫範例很方便,請大家自行到以下網址下載最新的 LINQPad:http://www.LINQpad.net/。
因為匿名委派是具名委派的進化版本,為了方便大家比對、感覺差異,所以先看一下昨天「LINQ自學筆記-打地基-具名委派」除蟲的範例:

//具名委派 
//宣告端 - 豪宅主人交代房子裡不能有蟲,不然就火掉我,所以我要先準備好蟲出沒的應變措施: 
public delegate string 除蟲(string 蟲); 
public class 豪宅 { 
    public void 蟲出沒(除蟲 人){ 
        Console.WriteLine(人("蟑螂")); 
    }
}
//邏輯端 - 執行業務的人 
public class 除蟲高手 { 
    public string 噴藥(string 蟲)  { 
        return "噴殺蟲劑讓 " + 蟲 + " 掛點。\n"; 
    } 
}
//呼叫端 - 這是管家 
void Main()  { 
  豪宅 白宮 = new 豪宅(); 
  除蟲高手 高手 = new 除蟲高手(); 
  除蟲 除蟲的人 = new 除蟲(高手.噴藥); 
  白宮.蟲出沒(除蟲的人); 
}

在呼叫端,.Net 2.0 先幫我們解決第一個麻煩的事情,就是不用自己再去建立委派的執行個體,由編譯器在編譯時期,幫我們建立委派類別的執行個體,所以呼叫端可以先精簡成:

//呼叫端 - 這是管家 
void Main()  { 
  豪宅 白宮 = new 豪宅(); 
  除蟲高手 高手 = new 除蟲高手(); 
  //除蟲 除蟲的人 = new 除蟲(高手.噴藥); 
  除蟲 除蟲的人 = 高手.噴藥; 
  白宮.蟲出沒(除蟲的人); 
}

這是很合理的事情,因為委派類別很特殊,我們要建立它的執行個體唯二的目的就是:設定關聯的方法和呼叫會引動委派的方法並把委派丟進去,所以建立執行個體這件事讓編譯器來做並不難,也節省撰寫程式的數量。請注意,我們強調的是編譯器幫我們建立委派執行個體,並不是不用建立執行個體喔,請別誤會。

但是在上述呼叫端的程式碼中,我們還會發現,如果建立的委派只有設定一個關聯方法,接下來其實只會也只能做一件事,就是把委派當參數丟給會引動委派的方法,也就是把委派塞給某個方法去調用。那麼建立委派、設定關聯方法、呼叫執行方法,其實應該可以一條龍處理就好,而且其實在這種情況下,委派的名稱不重要,因此微軟也幫我們考量到了:

//呼叫端 - 這是管家 
void Main()  { 
  豪宅 白宮 = new 豪宅(); 
  除蟲高手 高手 = new 除蟲高手(); 
  //除蟲 除蟲的人 = new 除蟲(高手.噴藥); 
  //除蟲 除蟲的人 = 高手.噴藥; 
  白宮.蟲出沒(高手.噴藥); 
}

這樣不只省去建立委派執行個體的程式,連建立委派的具名變數都省掉了,這就是「匿名委派」(Anonymous Delegate)。
PS. 匿名委派又稱之為匿名方法(Anonymous Method)。

再來講故事,在具名委派程式碼所描述的情境中,在真實情況裡有可能是這樣:

  1. 管家手下那麼多人,其實並不需要找外面的人來處理蟲的問題。
  2. 如果只是噴藥而已,管家並不用找特定人來處理,自己隨手拿藥來噴,或那個倒楣佣人正好經過,叫他去噴就好。
  3. 除蟲其實也不見得只有噴藥這個手段,有可能是投藥餌或其他除蟲手段。

總結上述幾點,所以對於管家來說,若不用找外包,只要從自己手下找人除蟲,那找什麼人、用什麼手段其實不重要,反正就是要有蟲出沒,就叫人把蟲幹掉而已,因此程式碼可以精簡成:

//具名委派 
//宣告端 - 豪宅主人交代房子裡不能有蟲,不然就火掉我,所以我要先準備好蟲出沒的應變措施: 
public delegate string 除蟲(string 蟲); 
public class 豪宅 { 
    public void 蟲出沒(除蟲 人){ 
        Console.WriteLine(人("蟑螂")); 
    }
}
//呼叫端 - 這是管家,手下那麼多人,不用外找,而且不一定要噴藥,也可以是其他手段 
void Main()  { 
  豪宅 白宮 = new 豪宅(); 
  白宮.蟲出沒(delegate(string 蟲) { 
                return "把 " + 蟲 + " 幹掉。\n";} 
              ); 
}

有沒有耳目一新的感覺!因為不用外包也不管是什麼人來除蟲,所以我們省去了建立邏輯端執行個體的程式碼;因為不管用什麼手段,只要達成目的即可,所以也省去撰寫方法名稱的部分,整個程式碼就精簡了。

最後這個範例一定要看懂,因為 LINQ 查詢中,大量使用了上述的方式,例如:

int[] numbers = new int[]{1,2,3,4}; 
var query = numbers.Where(delegate(int n) { 
                                             return n % 2 == 0;}); 
foreach (var n in query) 
{ 
    Console.WriteLine(n); 
}
//輸出:2 4

第一行是資料來源,第二行是 LINQ 語法,目的是從資料來源中取出 2 的倍數,最後利用 foreach 迴圈,逐一把結果輸出。當我們了解匿名型別,以後看到這種 LINQ 語法,應該都能看懂了。

PS. 上述語法可以透過型別推斷和 LINQPad 的 Dump 語法再精簡,不過為了方便閱讀程式,所以刻意保留較完整的寫法。

這一篇大幅精簡了委派中,呼叫端的程式碼,甚至把邏輯端也搬進呼叫端,在調用方法的同時,以匿名委派的方式同時把邏輯定義當參數傳遞進去,讓整個委派實用價值更高。下一篇,我們將來看看宣告端是否也可以用類似的方法精簡程式碼。


上一篇
LINQ自學筆記-打地基-具名委派
下一篇
LINQ自學筆記-打地基-泛型委派
系列文
分享一些學習心得30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言