这两天在家学习,查询了一些关于delegate的用法及相关知识,现将自己的一些体会以及网上精华写出来,以便于将来所用。
现在我们来看看,究竟什么是委托?
介绍:
Delegate,中文翻译为代理或委托(我更倾向于委托),是一种可以把引用存储为函数的类型,其本质就是函数指针。
对微软来说,一个delegate声明指定了一个相关的类型,你可以使用这一类型来封装一个含有特定标记的方法。一个delegate实例可以封装一个静态或一个实例方法。概略的说,Delegates就好象是C++中的函数指针;然而,delegate属于安全和保护类型。
既然说了Delegate 是一种函数指针,但与普通的函数指针相比,区别主要有三:
1) 一个 delegate object 一次可以搭载多个方法(methods),而不是一次一个。当我们唤起一个搭载了多个方法(methods)的 delegate,所有方法以其“被搭载到 delegate object 的顺序”被依次唤起——稍候我们就来看看如何这样做。
2) 一个 delegate object 所搭载的方法(methods)并不需要属于同一个类别。一个 delegate object 所搭载的所有方法(methods)必须具有相同的返回类型和参数,这是由委托的声明所决定的。然而,这些方法(methods)可以即有 static 也有 non-static,可以由一个或多个不同类别的成员组成。
3) 一个 delegate type 的声明在本质上是创建了一个新的 subtype instance,该 subtype 派生自 .NET library framework 的 abstract base classes Delegate 或 MulticastDelegate,它们提供一组 public methods 用以询访 delegate object 或其搭载的方法(methods)
委托的声明非常类似于函数,但不带函数体,且要使用delegate关键字。委托的声明指定了一个函数签名,其中包含了一个返回类型和参数列表。
其声明格式如下:
C#使用关键字delegate来声明委托类型:
[访问修饰符] delegate 结果类型 委托标识符([形参列表]);
委托类型可以在声明类的任何地方声明。
在声明了委托之后,就可以初始化一个委托变量为与委托有相同签名(返回类型和参数类型及数目)的函数引用。之后就可以使用委托变量调用这个函数,就想该变量是一个函数一样。
我们来看看是如何实例化一个委托变量的:
委托使用new运算符来实例化。
新创建的委托实例所引用的对象为以下之一:
⑴委托创建表达式中引用的静态方法
⑵委托创建表达式中引用的目标对象(此对象不能为null)和实例方法
⑶另一个委托
例如:
Code
delegate void MyDelegate(int x);
class MyClass
{
public static void Method1(int i)
{
}
public void Method2(int i)
{
}
}
class TestClass
{
static void Main()
{
//静态方法
MyDelegate delegate1=new MyDelegate(MyClass.Method1);
//实例方法
MyClass class1=new MyClass();
MyDelegate delegate2=new MyDelegate(class1.Method2);
//另一个委托
MyDelegate delegate3=new MyDelegate(delegate2);
}
}
初始化委托变量为一个函数引用之后,就可以通过委托变量执行该函数的方法了。在调用时,有一点必须注意:被调用的函数的参数及返回类型必须与委托声明时定义的内容保持一致。
例如:
Code
namespace delegateTest
{
public delegate int mydelegateTest(int i,int j);
class calculate
{
public static int add(int i,int j)
{
return i+j;
}
public static int minus(int i,int j)
{
return i-j;
}
}
class delegateapp
{
static void Main(string[] args)
{
mydelegateTest d0=new mydelegateTest(calculate.add);//声明一个mydelegateTest的实例d0,并用calculate.add对其进行初始化,实际上就是将委托与方法链接起来。
int i=d0(99,1);//开始调用委托,就像是使用静态成员方法calculate.add(int i,int j)一样。
System.Console.WriteLine("这是运行add的结果:{0}",i);
mydelegateTest d1=new mydelegateTest(calculate.minus);
int j=d1(100,99);
System.Console.WriteLine("这是运行minus的结果:{0}",j);
System.Console.ReadLine();
}
}
}
通过以上的示例,我想大家已经明白了,委托究竟是做什么的,其实质是通过创建一个函数的委托变量,来执行这个函数。就好比一个人带着面具,受我们委托,去帮我们做本应该由我们自己来做的事情。那为什么要这样做呢,自己直接执行函数不好么?
这就提出了下一个问题:
为什么要使用委托,使用委托有什么好处?
简单地回答:增加一层间接层来实现晚绑定,从而获得更为松散的耦合。
系统地回答:
可参考文章:J2EE核心模式-Business Delegate(业务代理)
URL:http://www.lifevv.com/sysdesign/doc/20080107211137248.html
这篇文章讲得很清楚,很专业,大家可以看这篇文章。
如何使用delegate呢?在哪些场合需要用到delegate?
现在我们来看看,使用delegate的几种用法:
总的来说,delegate的使用可以分为两种类型:一种是直接通过delegate来调用某个函数;另一种是在自定义事件中用到,这也是委托用得最普遍的一种方式。对于第一种委托使用类型,意义不是很大,因为在很多场合下,我们都可以通过直接调用函数方法来完成某项功能,而使用委托来执行函数功能,必然是因为在某些时候,我们不便于直接使用函数来执行,而需要通过委托间接执行函数来达到目的。所以,这里我们著重讲一下在后一种用法。
先来看看单独使用委托的用法。
前面我们已经讲述了委托执行函数调用的方法。这里我们讲一下,一个委托同时执行多个函数的情况。
先看个例子:
Code
delegate void EatDelegate(string food);
class MyDelegateClass
{
static void zsEat(string food)
{
Console.WriteLine("张三吃"+food);
}
static void lsEat(string food)
{
Console.WriteLine("李四吃"+food);
}
static void wwEat(string food)
{
Console.WriteLine("王五吃"+food);
}
static void main()
{
EatDelegate zs = new EatDelegate(zsEat);
EatDelegate ls = new EatDelegate(lsEat);
EatDelegate ww = new EatDelegate(wwEat);
Console.WriteLine("张三、李四、王五开座谈会");
EatDelegate eatDelegateChain = zs+ls+ww;
eatDelegateChain("西瓜");
Console.WriteLine("李四出去接电话");
eatDelegateChain -= ls;
eatDelegateChain("香蕉");
Console.WriteLine("李四回来了");
eatDelegateChain += ls;
eatDelegateChain("桔子");
}
}
通过上述代码,我们可知,如果一个委托需要同时执行多个同类型的函数,只需要通过委托链来实现,通过构建一个委托对象,借助+=和-=运算符,就可实现多个函数对象的添加和删减。只需一次性执行委托链,即可同时执行多个函数。
看完上述部分,现在我们来看看,在事件处理时,利用委托来实现回调处理。这也是委托最大的用处和最常用的方式。
我们先来看看事件,以了解为什么要在事件处理机制中用到委托。
事件类似于异常,因为它们都由对象引发。在处理事件时,我们不会通过try…catch类似的结构来处理事件,而必须订阅它们,订阅的含义就是,通过写好的事件处理函数,在发生这些事件时的进行处理。事件处理程序本身都是很简单的函数,其惟一限制是它必须匹配于事件所要求的签名(返回类型和参数)。这个签名是事件定义的一部分,是由一个委托在声明时所指定的。
我们先来看一下一个示例:
假设有一个出版社,出版两种杂志:《计算机》和《生活》。当订阅者订阅了某种杂志后,每当杂志社定期发行杂志时,订阅者都将收到对应的杂志。
Code
class Publisher //出版社
{
public delegate void PublishComputer(string magazineName);
public delegate void PublishLife(string magazineName);
//在event之后指明了PublishComputer,即表示该事件发生时,由PublishComputer委托其它函数来处理
public event PublishComputer OnPubComputer;
public event PublishLife OnPubLife;
public void issueComputer()
{
if(OnPubComputer!=null)
{
Console.WriteLine("发行《电脑》杂志");
OnPubComputer("电脑杂志");
}
}
public void issueLife()
{
if(OnPubLife!=null)
{
Console.WriteLine("发行《生活》杂志");
OnPubLife("生活杂志");
}
}
}
class Subscriber //订阅者
{
private string name;
public Subscriber(string name)
{
this.name = name;
}
public void receive(string magazineName)
{
Console.WriteLine(name+"已经收到"+magazineName);
}
}
class Story
{
static void main()
{
Publisher Pub = new Publisher();
Subscriber zs = new Subscriber("张三");
Subscriber ls = new Subscriber("李四");
//在这里,其实就是通过委托来实现事件的回调处理
Pub.OnPubComputer +=new Publisher.PublishComputer(zs.receive); //张三订阅了电脑杂志
Pub.OnPubComputer +=new Publisher.PublishComputer(ls.receive); //李四订阅了电脑杂志
Pub.OnPubLife += new Publisher.PublishLife(ls.receive); //李四订阅了生活杂志
//发行杂志,将触发订阅事件
Pub.issueComputer();
Pub.issueLife();
Console.WriteLine("一年后");
Pub.OnPubComputer -= new Publisher.PublishComputer(ls.receive); //李四不再订阅电脑杂志
//发行杂志,将触发订阅事件
Pub.issueComputer();
Pub.issueLife();
}
}
通过以上代码,我们可以看到,通过PublishComputer 这一个委托,我们就实现了张三和李四两个人收到计算机杂志的事件。这表明,对于同一事件,我们可以利用委托来实现多个处理函数,它们分别为多个对象服务。这就是委托在自定义事件中的最大好处。
当然,在这个过程中,我们也会发现,我们所声明的PublishComputer和PublishLife委托,都是void类型的返回值,以至于其事件处理函数都为void返回类型。我们不禁要问,是否可以为事件和委托提供返回类型呢?答案是:可以,但这样会存在一个问题。这是因为引发给定的事件时,可能会调用好几个事件处理程序,如果这些处理程序都返回一个值,那么我们该使用哪个返回值呢?系统处理这个问题的方式是,只允许访问由事件处理程序最后返回的那个值,也就是最后一个订阅该事件的处理程序返回的值。
就是这么简单,结合delegate的实现,我们可以将自定义事件的实现归结为以下几步:
1.定义delegate对象类型,它有两个参数,第一个参数是事件发送者对象,第二个参数是事件参数类对象。
2.定义事件参数类,此类应当从System.EventArgs类派生。如果事件不带参数,这一步可以省略。
3.定义事件处理方法,它应当与delegate对象具有相同的参数和返回值类型。
4. 用event关键字定义事件对象,它同时也是一个delegate对象。
5.用+=操作符添加事件到事件队列中(-=操作符能够将事件从队列中删除)。
6.在需要触发事件的地方用调用delegate的方式写事件触发方法。一般来说,此方法应为protected访问限制,既不能以public方式调用,但可以被子类继承。名字是OnEventName。
7. 在适当的地方调用事件触发方法触发事件。
Code
using System;
public class EventTest
{
// 步骤1,定义delegate对象
public delegate void MyEventHandler(object sender, System.EventArgs e);
// 步骤2省略
public class MyEventCls
{
// 步骤3,定义事件处理方法,它与delegate对象具有相同的参数和返回值类// 型
public void MyEventFunc(object sender, System.EventArgs e)
{
Console.WriteLine("My event is ok!");
}
}
// 步骤4,用event关键字定义事件对象
private event MyEventHandler myevent;
private MyEventCls myecls;
public EventTest()
{
myecls = new MyEventCls();
// 步骤5,用+=操作符将事件添加到队列中
this.myevent += new MyEventHandler(myecls.MyEventFunc);
}
// 步骤6,以调用delegate的方式写事件触发函数
protected void OnMyEvent(System.EventArgs e)
{
if(myevent != null)
myevent(this, e);
}
public void RaiseEvent()
{
EventArgs e = new EventArgs();
// 步骤7,触发事件
OnMyEvent(e);
}
public static void Main()
{
EventTest et = new EventTest();
Console.Write("Please input ''a'':");
string s = Console.ReadLine();
if(s == "a")
{
et.RaiseEvent();
}
else
{
Console.WriteLine("Error");
}
}
}
以上内容主要是围绕delegate委托和事件来展开的,方便大家快速理解什么是委托以及其用法。希望大家提出好的建议!