详解C#中委托,事件与回调函数讲解
.Net编程中最经常用的元素,事件必然是其中之一。无论在ASP.NET还是WINFrom开发中,窗体加载(Load),绘制(Paint),初始化(Init)等等。
“protectedvoidPage_Load(objectsender,EventArgse)”这段代码相信没有人不熟悉的。细心一点一定会发现,非常多的事件方法都是带了“objectsender,EventArgse”这两个参数。这是不是和委托非常相似呢?
一、委托(有些书中也称为委派)
委托是什么呢?这个名字的意思已经赋予了我们想象的空间,你是编程的,你现在正在写一个ASP.NET网页,而JS是你不熟悉的,于是你委托你的一位同事来帮助你完成JS部分。这就是委托,把你所不能做的事情交给其他人去做。而怎么知道是哪个人去做呢?当然是要知道名字!而为了区别名字一样的不同人,因此,需要描述一个特征。
在C#中,委托的作用是这样描述的:委托就像一个函数的指针,在程序运行时可以使用它们来调用不同的函数。这个其实和你委托同事完成JS代码一样。如果有两位同事可以做这件事情,他们只要做的结果能够满足你的需求(就像一个接口),尽管他们做的过程不一样,并且作出的效果也不一样,但是,能够达到你的要求就可以了。
1、简单的委托
那委托需要承载哪些信息呢?首先,它存储了方法名,还有参数列表(方法签名),以及返回的类型。比如:
delegatestring/*返回类型*/ProcessDelegate(inti);
这就是一个委托的定义。蓝色部分是声明委托的关键字,红色部分是返回的类型,而黑色部分是委托的类型名,和一个类名差不多,而()里的就是参数部分。它的意思是,你要使用这个委托来做事情的话,那么,做事情的方法必须满足以下条件:
1、返回类型和委托的返回类型一致,这里是string类型;
2、能且只能有一个参数,并且是int类型。
OK,满足以上两个条件,一切就可以工作了:)
例如:
usingSystem;
usingSystem.Collections.Generic;
usingSystem.Text;
namespaceTestApp
{
///<summary>
///委托
///</summary>
///<paramname="s1"></param>
///<paramname="s2"></param>
///<returns></returns>
publicdelegatestringProcessDelegate(strings1,strings2);
classProgram
{
staticvoidMain(string[]args)
{
/*调用方法*/
ProcessDelegatepd=newProcessDelegate(newTest().Process);
Console.WriteLine(pd("Text1","Text2"));
}
}
publicclassTest
{
///<summary>
///方法
///</summary>
///<paramname="s1"></param>
///<paramname="s2"></param>
///<returns></returns>
publicstringProcess(strings1,strings2)
{
returns1+s2;
}
}
}
输出的结果是:
Text1Tex2
2、泛型委托
泛型的委托,就是然参数的类型不确定,例如代码改写为:
usingSystem;
usingSystem.Collections.Generic;
usingSystem.Text;
namespaceTestApp
{
///<summary>
///委托
///</summary>
///<paramname="s1"></param>
///<paramname="s2"></param>
///<returns></returns>
publicdelegatestringProcessDelegate<T,S>(Ts1,Ss2);
classProgram
{
staticvoidMain(string[]args)
{
/*调用方法*/
ProcessDelegate<string,int>pd=newProcessDelegate<string,int>(newTest().Process);
Console.WriteLine(pd("Text1",100));
}
}
publicclassTest
{
///<summary>
///方法
///</summary>
///<paramname="s1"></param>
///<paramname="s2"></param>
///<returns></returns>
publicstringProcess(strings1,ints2)
{
returns1+s2;
}
}
}
输出的结果就是:
Text1100
泛型的详细内容不属于本文的介绍范围,这里不加多说了。
二、事件
在某件事情发生时,一个对象可以通过事件通知另一个对象。比如,前台完成了前台界面,他通知你,可以把前台和你开发的程序整合了。这就是一个事件。可以看出事件是在一个时间节点去触发另外一件事情,而另外一件事情怎么去做,他不会关心。就事件来说,关键点就是什么时候,让谁去做。
在C#中,时间定义关键字是event。例如:
eventProcessDelegateProcessEvent;
整个事件定义方法以及执行过程:
usingSystem;
usingSystem.Collections.Generic;
usingSystem.Text;
namespaceTestApp
{
///<summary>
///委托
///</summary>
///<paramname="s1"></param>
///<paramname="s2"></param>
///<returns></returns>
publicdelegatevoidProcessDelegate(objectsender,EventArgse);
classProgram
{
staticvoidMain(string[]args)
{
/*第一步执行*/
Testt=newTest();
/*关联事件方法,相当于寻找到了委托人*/
t.ProcessEvent+=newProcessDelegate(t_ProcessEvent);
/*进入Process方法*/
Console.WriteLine(t.Process());
Console.Read();
}
staticvoidt_ProcessEvent(objectsender,EventArgse)
{
Testt=(Test)sender;
t.Text1="Hello";
t.Text2="World";
}
}
publicclassTest
{
privatestrings1;
publicstringText1
{
get{returns1;}
set{s1=value;}
}
privatestrings2;
publicstringText2
{
get{returns2;}
set{s2=value;}
}
publiceventProcessDelegateProcessEvent;
voidProcessAction(objectsender,EventArgse)
{
if(ProcessEvent==null)
ProcessEvent+=newProcessDelegate(t_ProcessEvent);
ProcessEvent(sender,e);
}
//如果没有自己指定关联方法,将会调用该方法抛出错误
voidt_ProcessEvent(objectsender,EventArgse)
{
thrownewException("Themethodoroperationisnotimplemented.");
}
voidOnProcess()
{
ProcessAction(this,EventArgs.Empty);
}
publicstringProcess()
{
OnProcess();
returns1+s2;
}
}
}
感觉到了什么?是不是和代码注入了差不多,相当于是可以用任意符合委托接口(委托确实很像接口)的代码,注入到Process过程。在他返回之前给他赋值。
三、回调函数
打了这么多字,好累啊!
回调函数就是把一个方法的传给另外一个方法去执行。在C#有很多回调函数,比如异步操作的时候。这里先举个例子:
usingSystem;
usingSystem.Collections.Generic;
usingSystem.Text;
namespaceTestApp
{
///<summary>
///委托
///</summary>
///<paramname="s1"></param>
///<paramname="s2"></param>
///<returns></returns>
publicdelegatestringProcessDelegate(strings1,strings2);
classProgram
{
staticvoidMain(string[]args)
{
/*调用方法*/
Testt=newTest();
stringr1=t.Process("Text1","Text2",newProcessDelegate(t.Process1));
stringr2=t.Process("Text1","Text2",newProcessDelegate(t.Process2));
stringr3=t.Process("Text1","Text2",newProcessDelegate(t.Process3));
Console.WriteLine(r1);
Console.WriteLine(r2);
Console.WriteLine(r3);
}
}
publicclassTest
{
publicstringProcess(strings1,strings2,ProcessDelegateprocess)
{
returnprocess(s1,s2);
}
publicstringProcess1(strings1,strings2)
{
returns1+s2;
}
publicstringProcess2(strings1,strings2)
{
returns1+Environment.NewLine+s2;
}
publicstringProcess3(strings1,strings2)
{
returns2+s1;
}
}
}
输出结果:
Text1Text2
Text1
Text2
Text2Text1
Process方法调用了一个回调函数,当然这里只执行了回调函数。可以看出,可以把任意一个符合这个委托的方法传递进去,意思就是说这部分代码是可变的。而设计上有一个抽离出可变部分代码的原则,这种用法无疑可以用到那种场合了。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。