1. 程式人生 > >C#語法小知識(四)委託delegate

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 ();
如果對委託模式(或者叫代理模式)比較熟悉的話,應該很容易就看懂這段程式碼。用C++的語法來解釋就是為TestDelegate設定了一個回撥。

但是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);