C#語法小知識(四)委託delegate
delegate 是一種可用於封裝命名或匿名方法的引用型別。委託類似於 C++ 中的函式指標;但是,委託是型別安全和可靠的。有關委託的應用,請參見委託和泛型委託。
這裡我們就介紹一下委託的幾種用法。
一個簡單的委託示例:
public class TestDelegate { public delegate void Delegate(object sender); private Delegate _delegate; public void SetDelegate(Delegate dlg) { _delegate = dlg; } public void InvokeDelegate() { if (_delegate == null) { return; } _delegate.Invoke (this); } }
實現委託介面:
public class TestListenerA
{
static public void OnDelegateInvoked(object sender)
{
System.Console.WriteLine ("Listener A");
}
}
用法:如果對委託模式<pre name="code" class="csharp"> TestDelegate dlg = new TestDelegate(); TestListenerA la = new TestListenerA (); dlg.SetDelegate (la.OnDelegateInvoked); dlg.InvokeDelegate ();
但是delegate的用法不僅限於此,我們為TestDelegate增加一段程式碼:
public void AddListener(Delegate dlg)
{
_delegate += dlg;
}
public void RemoveListener(Delegate dlg)
{
_delegate -= dlg;
}
這樣我們就可以為TestDelegate增加多個回撥。
在宣告一個類,實現了static的委託:
public class TestListenerB
{
static public void EventHandler(object sender)
{
System.Console.WriteLine ("Listener B");
}
}
那麼我們就可以這樣使用delegate:
TestDelegate dlg = new TestDelegate();
TestListenerA la = new TestListenerA ();
dlg.AddListener (la.OnDelegateInvoked);
dlg.AddListener (TestListenerB.EventHandler);
TestDelegate.Delegate lamdaDlg = ((object sender) => {
System.Console.WriteLine ("Listener Lamda C");
});
dlg.AddListener (lamdaDlg);
dlg.AddListener((object sender) =>
{
System.Console.WriteLine ("Listener Lamda D");
});
dlg.InvokeDelegate ();
我們還可以去掉部分delegate:
dlg.RemoveListener (TestListenerB.EventHandler);
dlg.RemoveListener (lamdaDlg);
dlg.InvokeDelegate ();
這樣TestListenerB.EventHandler和lamdaDlg就不再被呼叫了。
我們還可以移除所有delegate:
dlg.SetDelegate (null);
並重新新增:
dlg.AddListener (lamdaDlg);
這裡可能會有疑問,讓null和lamdaDlg相加不會有問題嗎?
我們就帶著這個問題深入瞭解一下delegate。
System.Delegate是一個抽象類(abstractclass),所以一些具體的實現都是在System.MulticastDelegate裡完成的。
我們這裡主要看兩個方法Combine和Remove,這兩個方法都是靜態方法,可以推測,這兩個方法分別呼叫了CombineImpl和RemoveImpl。
CombineImpl是合併委託的實現,而RemoveImpl是移除委託的實現。
而System.Delegate和System.MulticastDelegate都有一個方法是GetInvocationList。這個方法返回了一個System.Delegate陣列,數組裡是參與呼叫的delegate。
由此我們可以推測,System.MulticastDelegate裡維護了一個委託表,當呼叫CombineImpl的時候會把新的委託加入這個表,當呼叫RemoveImpl的時候會把目標委託從表裡刪除。
那麼我們就可以推測,當delegate呼叫operator + 的時候就會呼叫System.Delegate的Combine方法,而呼叫operator -的時候就會呼叫System.Delegate的Remove方法。而給一個delegate賦值的時候,便會呼叫System.Delegate的CreateDelegate方法。
而回到之前的問題,為什麼null和lamdaDlg可以相加?我們不必探究Combine方法是如何實現的,只需要簡單的轉換一下便可以理解
_delegate += dlg;
等同於:
_delegate = (TestDelegate.Delegate)System.Delegate.Combine (_delegate, dlg);