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。
我们知道所有的delegate都继承自System.MulticastDelegate,而后者继承自System.Delegate。
System.Delegate是一个抽象类(abstract class),所以一些具体的实现都是在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);