1 含义
委托(delegate)本质来说就是一个指向方法(method)的指针(pointer)。
通过这个委托(指针)可以调用(invoke)一个方法;也可以将委托作为一个方法(称为B方法)参数,在调用B方法时将一个委托实例传入(最终实现了将一个方法传递进入另一个方法中)。
2 声明
delegate
是一个用于声明委托的关键字。
委托的声明与定义方法类型,只是需要多增加delegate
关键字,且没有方法体。
public delegate void Del(string message);
// Create a method for a delegate.
public static void DelegateMethod(string message)
{
System.Console.WriteLine(message);
}
// Instantiate the delegate.
Del handler = DelegateMethod;
// Call the delegate.
handler("Hello World");
委托的签名(参数和返回值类型)必须和待委托方法的签名(参数和返回值类型)完全一致。
3 例子
例子1
public class MrZhang
{
//其实买车票的悲情人物是小张
public static void BuyTicket()
{
Console.WriteLine("NND,每次都让我去买票,鸡人呀!");
}
}
//小明类
class MrMing
{
//声明一个委托,其实就是个“命令”
public delegate void BugTicketEventHandler();
public static void Main(string[] args)
{
BugTicketEventHandler myDelegate1 = new BugTicketEventHandler(MrZhang.BuyTicket);
// 在C# 2.0中,加入了一种更简洁的表达
BugTicketEventHandler myDelegate2 = MrZhang.BuyTicket;
myDelegate1();
myDelegate2();
// 执行结果是BugTicketEventHandler()被调用了两次
Console.ReadKey();
}
}
这里的执行结果是BugTicketEventHandler()
被调用了两次(输出了两次NND,每次都让我去买票,鸡人呀!
),
注意,这里委托的签名(public delegate void BugTicketEventHandler();
)和待委托方法的签名(public static void BuyTicket()
)完全一致的。
例子2
在C# 1.0
和此之前,委托仅能被这样表达:
// Declare a delegate.
delegate void Del(string str);
// Declare a method with the same signature as the delegate.
static void Notify(string name)
{
Console.WriteLine("Notification received for: {0}", name);
}
// Create an instance of the delegate.
Del del1 = new Del(Notify);
在C# 2.0
中,加入了一种更简洁的表达方式。即,直接用函数的名称赋值给委托实例。
// C# 2.0 provides a simpler way to declare an instance of Del.
Del del2 = Notify;
同时,也可以使用匿名函数(anonymous method)来声明并初始化一个委托实例。
// Instantiate Del by using an anonymous method.
Del del3 = delegate(string name)
{ Console.WriteLine("Notification received for: {0}", name); };
在C# 3.0
中,增加了可以用Lambda表达式(Lambda expression)来实例化委托实例的方式。
// Instantiate Del by using a lambda expression.
Del del4 = name => { Console.WriteLine("Notification received for: {0}", name); };
委托的应用
如前面提到的,我们也可以使用委托,以实现将一个A方法(作为一个B方法的调用参数)传递到B方法内部。回调(callback)则是这样的一种典型的使用方式。
public delegate void Del(string message);
public void MethodWithCallback(int param1, int param2, Del callback)
{
callback("The number is: " + (param1 + param2).ToString());
}
//output: The number is: 3
MethodWithCallback(1, 2, handler);
说明
我们不能将C#中的委托(delegate)完全理解成C语言中的函数指针(即,它允许你传递一个方法到另一个方法)。
delegate
有许多函数指针不具备的特点:
特点1:
函数指针只能指向静态函数
,而委托既能指向(引用)静态函数(在C#中以static
修饰的函数),也可以引用非静态成员函数。
// Declare a delegate
delegate void Del();
class SampleClass
{
public void InstanceMethod()
{
System.Console.WriteLine("A message from the instance method.");
}
static public void StaticMethod()
{
System.Console.WriteLine("A message from the static method.");
}
}
class TestSampleClass
{
static void Main()
{
SampleClass sc = new SampleClass();
// Map the delegate to the instance method:
Del d = sc.InstanceMethod;
d();
// Map to the static method:
d = SampleClass.StaticMethod;
d();
}
}
/* Output:
A message from the instance method.
A message from the static method.
*/
在引用非静态成员函数时,委托不仅需要保存对此函数入口地址的引用,还需要保存该函数对应的类实例对象的引用。
特点2:
与函数指针相比,委托是面向对象、类型安全、可靠的受控(managed)对象。
也就是说,runtime
能够保证委托指向一个有效的方法,因此你无需担心委托指向了无效的地址或者越界地址。
4 多播委托(Multicast Delegates)
委托是一个指向方法的指针,而与普通指针唯一不同的是,委托可以同时指向一个或多个不同的方法,这就是多播委托(Multicast Delegates)。
public class MethodClass
{
public void Method1(string message) { }
public void Method2(string message) { }
}
MethodClass obj = new MethodClass();
Del d1 = obj.Method1;
Del d2 = obj.Method2;
Del d3 = DelegateMethod;
// step1
//Both types of assignment are valid.
Del allMethodsDelegate = d1 + d2;
allMethodsDelegate += d3;
// step2
//remove Method1
allMethodsDelegate -= d1;
// copy AllMethodsDelegate while removing d2
Del oneMethodDelegate = allMethodsDelegate - d2;
我们可以仅仅简单地通过+
和-
将不同的方法在一个委托的调用列表(invocation list)中添加或删除。
在这个例子中,经过step1
后,委托实例allMethodsDelegate
指向了三个方法(Method1(),
Method2(),
DelegateMethod()),这意味着如果此时调用委托实例(通过
allMethodsDelegate()`的方式),这三个方法会依次被执行。
在经过step2
后,此时如果调用委托实例,则只有两个方法(Method2()
,DelegateMethod()
)会被依次执行。
多播委托的应用
**多播委托被广泛应用在事件处理(event handling)中。**即,事件源对象(event source object)发送事件通知(event notification)给已经注册了该事件的事件接受者对象。
当然,在此之前。事件接受者对象首先需要创建一个响应该事件对应的处理方法,再声明一个指向该处理方法的委托,并将该委托实例提供给事件源对象。
这样之后,当该事件发生时,事件源对象就可以通过调用委托,从而调用事件接受者对应的事件响应方法(以完成事件通知)。