我的腦子裡有個名詞一直在糾結:委托。
顧名思義,委托,把事情托付給他人或機構(辦理)。造句諸如:“當事人委托律師出庭辯護”,“我能委托你辦一件事嗎”。 很明顯,委托是個抽象動作(Action),目的具體不詳,“出庭辯護”,“辦一件事”才是真正要做的事。但C#中委托卻讓我之前一頭霧水,因為這個概念從來未有如此被擺上台面。
我確信在以往的JavaScript 編程中,有類似“委托”這個概念的,比如按鈕事件綁定,匿名函數。而網上搜羅有關C# 委托的言語也大多與函數指針、事件綁定有關。下面將用JavaScript 與C# 兩種“委托”相對比,用於加深理解。
先看JavaScript中,給按鈕定義事件的方法:
1 <input type="button" value="Hello" id="btn" />
2 <script type="text/javascript">
3 //方法一:
4 function SayHello()
5 {
6 alert("Hello world!");
7 }
8 document.getElementById("btn").attachEvent("onclick", SayHello);
9
10 //方法二,匿名函數
11 document.getElementById("btn").attachEvent("onclick", function(){alert("Hello world")});
12 </script>
方法一是把一個現有的函數引用(類指針)賦值給按鈕的事件,而方法二是在給按鈕定義事件時,就地定義一個匿名函數。這兩種方法其實是一致的,如果把SayHello 的定義方式換用另外一種方式,將會發現,這一切不過是代碼的游戲:
var SayHello = function(){alert("Hello world!")};
方法一與方法二不過是引用上面代碼等號的左邊與右邊而已!
再看看C#:
1 protected void Page_Load(object sender, EventArgs e)
2 {
3 btn.Click += new System.EventHandler(btn_Click);
4 }
5
6 void btn_Click(object sender, EventArgs e)
7 {
8 //do sth
9 }
這是眾所熟悉的代碼,btn 是頁面上的一個按鈕,+= 表示在原有的基礎上續加一個事件,這與IE下的attachEvent(FF下的addEventLisener) 同出一轍。System.EventHandler 實際上就是一個委托,通過ILSpy 查看這個類:
1 using System;
2 using System.Runtime.InteropServices;
3 namespace System
4 {
5 [ComVisible(true)]
6 [Serializable]
7 public delegate void EventHandler(object sender, EventArgs e);
8 }
9
10 //構造函數:
11 // System.EventHandler
12 public extern EventHandler(object @object, IntPtr method);
因此,給按鈕增加事件,是先創建一個委托,委托再指定需要調用的函數(如btn_Click)。
通過對比兩種語言在以上事件定義實例上,C# 非常細致地抽象出這個動作,把委托與事件函數區分開來,這是JavaScript比較模糊的地方(或稱為JS的靈活性?)。
再看另外一種場合,JQuery 可以這樣遍歷數組:
1 <script type="text/javascript">
2 function DoSth(obj, i)
3 {
4 /*do sth*/
5 }
6 $("#box ul li").each(DoSth);
7
8 //或者這樣:
9 $("#box ul li").each(function(obj, i){/*do sth*/});
10 </script>
以上代碼的$() 函數返回的是元素數組,echo 方法的作用是遍歷並處理此數組。那它是怎樣做這樣的效果呢? 它把另外一個函數(假設為函數DoSth)當作參數,傳到echo 內部,所有處理動作都由函數DoSth來完成,並且默認規定好了接口,函數DoSth只能接收兩個參數,第一個是數組中的元素(弱類型),第二個是整型計數器。
把一個函數當地另外一個函數的參數,這樣的案例在原生的JavaScript 還有如:replace(/re/, function($1){}),array.sort(function(x){}),用法一致,不再討論。
再看看C#:
1 protected void Page_Load(object sender, EventArgs e)
2 {
3 List<string> Arr = new List<string>() { "2011年","10月","22日"};
4 //方法一
5 Arr.ForEach(delegate(string txt)
6 {
7 Write(txt);
8 });
9
10 //方法二
11 Arr.ForEach(new Action<string>(Write));
12
13 //方法三
14 Arr.ForEach(Write);
15 }
16
17
18 private void Write(string txt)
19 {
20 Response.Write(txt);
21 }
上面的代碼與JavaScript 何其相似。方法一就地定義委托,並定義委托的實際內容。方法二采用C#3.0的Action<T> 委托,它是delegate 類的泛型重載。方法三是編譯器給我們提供的便利,它是方法二的簡寫。
行文至此,委托的概念頓時明晰,混沌的思維豁然開朗,揭開面紗,原來早已熟悉多時,猛拍腦門,狠掐大腿......
作者:阿蔡