【C#】delegate 和 event

來源:互聯網
上載者:User

標籤:style   blog   http   color   io   os   ar   使用   sp   

在開始之前,先說一下文章的表達習慣。

Object a = new Object();

在上面的例子裡,Object 是一種類型,a 是一個引用,new Object() 構造了一個對象,有時 ”構造對象“ 也被稱為 ”建立執行個體“。有的文章習慣把 a 也稱作執行個體,請根據上下文理解不要混淆。接下來你會經常看到類似 ”Object 類型“ ”Object 引用“ ”Object 對象“ 這樣的表達。

另外,我會盡量使用 ”方法“ 而不使用 ”函數“ 的稱呼。習慣使用 C/C++ 的讀者接下來看到 ”方法“ 時不要感到困惑。

 

delegate 簡介

delegate 的中文意思是委託,在 c# 裡也是一個關鍵字。在 System 命名空間裡,有 Delegate 類 和 MulticastDelegate 類,後者繼承前者。編譯器和其他工具可以從 MulticastDelegate 派生,但是我們無法顯式的去繼承這兩個類。為了使用 delegate,我們需要使用 delegate 關鍵字來聲明一個 delegate 類型:

public delegate int MyDelegate(int arg);

和其他類型一樣,這個聲明可以放在一個命名空間內部或其他類的內部。我們只需要寫這樣一行代碼,編譯器為我們實現細節。注意 delegate 關鍵字後面的部分,可以是任何合理的方法簽名(方法簽名由方法的參數列表和傳回值組成,這兩者都相同的方法具有相同的簽名)。

現在我們已經聲明了一個 delegate 類型 MyDelegate,接下來用這個類型建立一個執行個體:

MyDelegate sample = new MyDelegate(Method);

看起來和建立其他類的執行個體沒有什麼區別,但是建構函式的參數 Method 是什麼呢?Method 必須是與 MyDelegate 聲明時的簽名一致的方法名稱,也就是說 Method 的簽名必須含有一個 int 參數和 int 傳回值(這樣描述並不準確,由於C#的協變和逆變的特性,詳細參考:委託中的協變和逆變)。delegate 是參考型別,所以如果沒有為 sample 初始化引用執行個體,那麼 sample 的值是 null。

現在有一個問題。如果 Method 是靜態方法,那麼這樣傳遞參數完全沒有問題;如果 Method 不是靜態方法,這樣就不一定正確了。我們知道普通方法必須由具體的對象來發起調用,所以傳遞一個普通方法給 delegate 的建構函式而不指定所屬對象是沒有道理的。如果上面這段代碼在類的普通方法裡,由於 this 關鍵字可以省略,所以 Method 被編譯器理解為 this.Method,所屬對象是明確的,所以仍是正確的;如果上面這段代碼在類的靜態方法裡,那麼會因為 Method 沒有明確的所屬對象而錯誤。這個細節需要留意。

最後我們需要瞭解如何調用 delegate 執行個體裡儲存的方法。

int a = sample(3);

看起來與直接調用 Method 沒有什麼區別。的確如此,僅需要注意 delegate 引用如果為 null 則會引起Null 參考異常。delegate 引用與其他類型的引用一樣,還可以執行 = 賦值運算、作為方法的參數傳遞、使用點運算子 . 訪問對象成員。

目前為止,您可能覺得 delegate 與 C/C++ 裡的函數指標很相似。但是,delegate 類型還可以執行 +/+= 和 -/-= 操作,因為 delegate 對象並不只是儲存一個方法的引用,而是儲存了一個方法列表。

sample -= Method;sample += Method;

- 用於從方法列表裡移除一個方法,+ 用於增加一個方法。+ 操作不會排除完全相同的方法引用。現在,sample(3) 的意義是依次調用方法列表裡的每個方法,最後一個方法的傳回值作為 sample(3) 的傳回值。

請注意類型轉換問題。在上面的代碼裡,我們把方法與 delegate 相加。其實,還可以直接把方法賦值給 delegate 引用。這些做法之所以合理,是因為方法可以隱式轉化為 delegate。

為了簡化編程,System 命名空間裡預聲明了一系列的泛型 delegate。使用這些聲明我們幾乎不再需要做額外的聲明工作了。

public delegate void Action();public delegate void Action<T>(T arg);public delegate void Action<T1, T2>(T1 arg1, T2 arg2);// 還有更多參數版本的 Actionpublic delegate TResult Func<TResult>();public delegate TResult Func<T, TResult>(T arg);public delegate TResult Func<T1, T2, TResult>(T1 arg1, T2 arg2);// 還有更多參數版本的 Func

 

匿名方法和 Lambda 運算式

在上文中,我們必須事先聲明 Method 方法才能初始化 delegate 執行個體。C# 提供了匿名方法來簡化 delegate 的初始化工作。

MyDelegate sample = delegate(int arg){    return arg * arg;};

看起來好像在運行代碼內部聲明了一個新方法並使用 sample 儲存。這樣做使得代碼更簡潔。注意匿名方法的傳回型別,必須是或可以隱式轉換為 delegate 聲明時簽名的傳回型別。匿名方法還有一個好處是可以訪問外部變數,匿名方法捕獲外部變數的引用而不是值,這一點需要留意,請看這段代碼:

int n = 0;sample = delegate(int arg) { return n * arg; };n = 1;print(sample(1));

結果將輸出 1 而不是 0。另外匿名方法裡無法訪問外部的 ref 或 out 參數。

Lambda 運算式是一種匿名函數。在 Lambda 運算子 => 的左邊指定輸入參數,在右邊指定執行運算式或語句。

sample = x => x * x;

看起來與上文的匿名方法非常相似。實際上匿名方法的所有限制和特徵在 lambda 運算式中同樣適用。

先看 Lambda 運算式的輸入參數部分:

// 無參數使用()() => DoSomeThing();// 一個參數直接寫出參數名arg => arg * arg;// 多個參數使用,分割(arg1, arg2) => arg1 == arg2;// 參數類型無法推斷時可以顯式指定(string arg1, char arg2) => arg1.Split(arg2);

在這些例子裡,=> 的右側都是單一運算式,如果需要傳回值,那麼運算式的值就是傳回值。使用大括弧可以書寫多條語句,但必須使用 return 明確指定傳回值。

(arg1, arg2) =>{    if(arg1 == arg2) return 0;    return arg1 > arg2 ? 1 : -1;}

 

event 簡介

event 中文意思是事件,在 c# 裡也是一個關鍵字。如下是 event 的聲明方式:

public event MyDelegate myEvent{    add { print("add: " + value); }    remove { print("remove: " + value); }}

看起來與屬性的聲明相似。event 具有兩種操作:+= 和 -=,右運算元必須為與 event 類型一致的 delegate(或 null)。+= 調用的是 add,-= 調用的是 remove。下面是調用的樣本:

myEvent += null;myEvent -= null;

結果列印出 "add: " 和 "remove: "。

有一個更簡單的聲明方法:

public event MyDelegate myEvent;

這種聲明類似於(實際並不相同):

private MyDelegate _myEvent;public event MyDelegate myEvent{    add { _myEvent += value; }    remove { _myEvent -= value; }}

使用這種簡單的聲明方法,你可以在 myEvent 所在的類中像對 delegate 一樣對 event 進行操作,包括 = 賦值運算和調用方法;但是在其他地方,你只能使用 += 和 -= 運算。這樣做的目的是,把所有可能不安全的操作封裝在類內部。對就是這樣,沒有更多了。

 

最後,文章的目的是儘可能用簡單的語言把委託和事件描述清楚,因此忽略了所有的非同步作業等相關細節。更細節的資料請參考 msdn 或這篇文章:C# in Depth: Delegates and Events。  

【C#】delegate 和 event

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.