ASP.NET深入浅出系列4- 也谈委托和事件
这个话题已经很多人谈过了,本不想重复拿出来说,但为了这个系列的完整性,我便把自己以前积累的东西整理了一下,大家全当消遣了。
一、委托的定义:
《高级汉语大词典》中是如下解释的:托付给别的人或机构办理。要说生活中的意思其实大家都能理解,无非是“当某人(机构)需要完成一件自己不能或不应该完成的事情的时候,此人(机构)物色一个合适的且有能力完成此事的人选,然后提供必要的信息,将此事委托给物色到的人(机构)来完成。” C#中的委托是一种引用方法的类型,一旦为委托分配了方法,委托将与该方法具有完全相同的行为,委托方法的使用可以像其他任何方法一样具有参数和返回值。委托对象能被传递给调用该方法引用的代码而无须知道哪个方法将在编译时被调用。委托是函数的封装,它代表一“类”函数。他们都符合一定的签名:拥有相同的参数列表、返回值类型。同时委托也可以看作是对函数的抽象,是函数的“类”。此时,委托实例代表一个具体的函数。委托应该和类同属一个层面,使用起来也很象一个类。我们先来看一个委托使用的实例:


<!--
Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/
-->publicdelegatevoidPrintHandler(stringstr);//声明委托类型
publicclassPrintStr
{
publicvoidCallPrint(stringinput)
{
Console.WriteLine(input);
}
}
staticvoidMain(string[]args)
{
PrintStrmyPrinter=newPrintStr();
PrintHandlermyHandler=null;
//将委托链接到方法,来实例化委托
myHandler+=newPrintHandler(myPrinter.CallPrint);
if(myHandler!=null)
myHandler("HelloWorld!");//调用委托,相当于匿名调用委托所链接的方法
myHandler-=newPrintHandler(myPrinter.CallPrint);
if(myHandler==null)
Console.WriteLine("myHandler==null");
Console.Read();
}
得到的结果为
Hello World!
myHandler==null
二、委托的特点
1、一个委托对象可以搭载多个方法。
2、一个委托对象搭载的方法并不需要属于同一个类,但所搭载的方法必须具有相同的原形和形式。
3、委托的实例将代表一个具体的函数
三、为什么要使用委托
1、更加灵活的方法调用。我们先举一个例子:


<!--
Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/
-->namespacedelegateEvent
{
publicclassBackupUtil
{
string_buFolder="c:\\backup\\";
//declarethedelegatetype
publicdelegateboolBackupFiles(stringfolder);
publicBackupFilesBackup;
publicBackupUtil(stringbackupType)
{
switch(backupType)
{
case"EXE":
Backup=newBackupFiles(BackupOnlyEXE);
break;
case"Folder":
Backup=newBackupFiles(BackupCurrentFolder);
break;
case"Today":
Backup=newBackupFiles(BackupAccessedToday);
break;
}
}
privateboolBackupOnlyEXE(stringfolder)
{
foreach(stringfileinDirectory.GetFiles(folder,"*.exe"))
{
FileInfofi=newFileInfo(file);
File.Copy(fi.FullName,_buFolder+fi.Name);
}
returntrue;
}
privateboolBackupCurrentFolder(stringfolder)
{
foreach(stringfileinDirectory.GetFiles(folder))
{
FileInfofi=newFileInfo(file);
File.Copy(fi.FullName,_buFolder+fi.Name);
}
returntrue;
}
privateboolBackupAccessedToday(stringfolder)
{
foreach(stringfileinDirectory.GetFiles(folder))
{
if(File.GetLastAccessTime(file).Date==DateTime.Now.Date)
{
FileInfofi=newFileInfo(file);
File.Copy(fi.FullName,_buFolder+fi.Name);
}
}
returntrue;
}
}
}
staticvoidMain(string[]args)
{
BackupUtilbu=newBackupUtil("Today");
boolret=bu.Backup("c:\\test\\");
Console.WriteLine(ret.ToString());
}
这个例子中客户端会根据不同的情况对文件进行备份
2、用于异步回调
由于实例化委托是一个对象,所以可以将其作为参数进行传递,也可以将其赋值给属性。这样方法就可以将一个委托作为参数来接受,并且以后可以调用该委托。这称为异步回调,是在较长的进程完成后用来通知调用方的常用方法,以这种方式使用委托时,使用委托的代码无需了解有关使用方法的实现方面的任何信息。
我们假设这样一个场景,我们希望有这样一个函数,对一个整型数组进行过滤,而过滤得条件在编写函数时还不知道,直到使用这个函数的时候可以根据当前的情况编写过滤条件函数,我们使用委托实现如下:


<!--
Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/
-->publicdelegateboolIntFilter(inti);
publicclassCommon
{
publicstaticint[]FilterArrayOfInts(int[]ints,IntFilterfilter)
{
ArrayListaList=newArrayList();
foreach(intiinints)
{
if(filter(i))
{
aList.Add(i);
}
}
return((int[])aList.ToArray(typeof(int)));
}
}
publicclassApplication
{
publicstaticboolIsOdd(inti)
{
return((i&1)==1);
}
}
staticvoidMain(string[]args)
{
int[]nums={1,2,3,4,5,6,7,8,9,10};
int[]oddNums=Common.FilterArrayOfInts(nums,Application.IsOdd);
foreach(intiinoddNums)
Console.WriteLine(i);
}
结果为:
1
3
5
7
9
3、多线程编程中使用委托来指定启动一个线程时调用的方法


<!--
Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/
-->publicstaticvoidThreadProc()
{
for(inti=0;i<10;i++)
{
Console.WriteLine("ThreadProc:{0}",i);
//Yieldtherestofthetimeslice.
Thread.Sleep(0);
}
}
publicstaticvoidMain()
{
Threadt=newThread(newThreadStart(ThreadProc));
}
4、C#中的事件模型。用他们指明处理给定事件的方法。
下面我们详细来介绍事件。
四、事件
事件允许类型(或者类型的实例)在某些特定事情发生的时候通知其他对象。
例如button类定义了一个名为click的事件,当buton对象被点击时,应用程序中的一些对象可能希望能够收到一个通知,并执行一些动作。但问题是当我们编写button类的时候并不知道到底要做什么,到底要调用哪些方法,我们只要在使用button的场景才知道到底应该在按下button时做些什么,基于这样的情况,我们想起了委托,我们先在button类中定义一个委托,然后在使用button的相应场景注册这个委托即可,似乎用委托就解决了这个问题了,那事件还有什么用呢,也就是说委托和事件的区别在哪里呢,其实事件就是一种特殊的委托,使用事件委托的类型,必须使用+=进行注册而不能用=,这样做的原因是事件中已经注册的方法不希望被新注册的方法覆盖掉。微软为使用事件定义了一个模式。
事件模式由以下五步组成:
1、定义一个类型用于保存所有需要发送给事件通知接受者的附加信息(继承于System.EventArgs)
2、定义一个委托类型,用于指定事件触发时被调用的方法类型
3、定义一个事件成员
4、定义一个受保护的虚方法,负责通知事件的登记对象
5、定义一个方法,将输入转化为期望的事件
我们这里举一个发Email的时候触发事件的例子:


<!--
Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/
-->publicclassMailManager
{
//1、定义一个类型用于保存所有需要发送给事件通知接受者的附加信息(继承于System.EventArgs)
publicclassMailMsgEvernArgs:EventArgs
{
publicMailMsgEvernArgs(stringfrom,stringto,stringsubject,stringbody)
{
this.from=from;
this.to=to;
this.subject=subject;
this.body=body;
}
publicreadonlystringfrom,to,subject,body;
}
//2、定义一个委托类型,用于指定事件触发时被调用的方法类型
publicdelegatevoidMailMsgEventHandler(objectsender,MailMsgEvernArgsargs);
//3、定义一个事件成员
publiceventMailMsgEventHandlerMailMsg;
//4、定义一个受保护的虚方法,负责通知事件的登记对象
protectedvirtualvoidOnMailMsg(MailMsgEvernArgse)
{
if(MailMsg!=null)
MailMsg(this,e);
}
//5、定义一个方法,将输入转化为期望的事件
publicvoidSimulateArrivingMsg(stringfrom,stringto,stringsubject,stringbody)
{
MailMsgEvernArgse=newMailMsgEvernArgs(from,to,subject,body);
OnMailMsg(e);
}
}
staticvoidMain(string[]args)
{
MailManagermm=newMailManager();
mm.MailMsg+=newMailManager.MailMsgEventHandler(mm_MailMsg);
mm.MailMsg+=newMailManager.MailMsgEventHandler(mm_MailMsg1);
mm.SimulateArrivingMsg("中国","美国","subject","body");
}
privatestaticvoidmm_MailMsg(objectsender,MailManager.MailMsgEvernArgsargs)
{
Console.WriteLine(args.from);
Console.WriteLine(args.to);
}
privatestaticvoidmm_MailMsg1(objectsender,MailManager.MailMsgEvernArgsargs)
{
Console.WriteLine(args.subject);
Console.WriteLine(args.body);
}