C#中的委托Delegate和事件Event例子解释Word格式文档下载.docx
《C#中的委托Delegate和事件Event例子解释Word格式文档下载.docx》由会员分享,可在线阅读,更多相关《C#中的委托Delegate和事件Event例子解释Word格式文档下载.docx(14页珍藏版)》请在冰豆网上搜索。
![C#中的委托Delegate和事件Event例子解释Word格式文档下载.docx](https://file1.bdocx.com/fileroot1/2023-2/5/0f27e630-4744-4966-a80e-fe1d5acc237b/0f27e630-4744-4966-a80e-fe1d5acc237b1.gif)
nowcallmax("
a<
"
b<
)..."
endl;
t
=
a>
b?
a:
b;
t<
}
min(int
nowcallmin("
typedef
(*myFun)(int
b);
//定义一个函数指针用来引用max,min
//回调函数
callback(myFunfun,
fun(a,b);
main()
i
10;
j
55;
callback(max,i,j);
callback(min,i,j);
Output:
nowcallmax(10,55)...
55
nowcallmin(10,55)...
10
Pressanykeytocontinue
输出的结果有可能另一些对函数指针不熟悉的朋友我些意外,我们并没在main()中显式调用max(),min()呀,怎么会调用到它们呢。
再仔细检查一下:
你可能发现了:
callback(max,i,j);
这个函数调用了max(),这下好了。
你便可以回答类似于这样的问题:
我怎么在一个函数(callback)体内调用[主调用函数中的函数(max或min)],最好能通过参数指入具体需要指定哪一个函数?
这便是函数指针的作用了,通过转入函数指针,可以很方便的回调(callback)另外一些函数,而且可以实现参观化具体需要回调用的函数。
2,Introducedelegateinc#;
.net里一向是"
忌讳"
提及"
指针"
的,"
很多程度上意味着不安全。
C#.net里便提出了一个新的术语:
委托(delegate)来实现类似函数指针的功能。
我们来看看在C#中怎么样实现上面的例子。
WinXP+C#invs2003
System;
Class1
{
class
ExcelProgram
static
Console.WriteLine("
nowcallmax({0},{1})"
a,b);
Console.WriteLine(t);
nowcallmin({0},{1})"
delegate
myFun(int
//定义一个委托用来引用max,min
[STAThread]
Main(string[]
args)
callback(new
myFun(max),i,j);
myFun(min),i,j);
Console.ReadLine();
其实代码上大同小异,除了几个static申明以外(C#除静态成员外必须要求对象引用),最大的变化要算定义"
函数指指"
,哦...不..不..不..应该是定义"
委托"
(小样穿上马甲了..).定义委托的语法如下:
delegatevoid
myFun(inta,intb);
其中delegate是关键字,myFun是委托名,剩下的是函数签名(signature).我们可以申明一个委托:
myFunMax=newmyFun(max);
那么上面的回调函数的代码便可以写成:
callback(Max,i,j);
3,Differencebetweenfunctionpointeranddelegate;
委托除了可以引用一个函数外,能力上还有了一些加强,其中有一点不得不提的是:
多点委托(Multicastdelegate).简单地讲就是可以通过一个申明一个委托,来调用多个函数,不信?
我们只要稍微更改一下上面的C#代码中的Main函数就可以了,类似:
staticvoidMain(string[]args)
inti=10;
intj=55;
myFunmulCast=newmyFun(max);
mulCast
+=
newmyFun(min);
//
(1)
callback(mulCast,i,j);
//callback(newmyFun(min),i,j);
输出如下:
没骗你吧,我们只用了一个委托mulCast便同时调用了max和min。
不知你注意到没有,上面代码的
(1)处用"
+="
给已经存在的委托(mulCast)又加了一个函数(min)。
这样看来C#中的委托更像一个函数指针链表。
实质是在C#中,delegate关键字指定的委托自动从System.MulticastDelegate派生.而System.MulticastDelegate是一个带有链接的委托列表,在callback中只需调用mulCast的引用便可以以同样的参数调用该链表中的所有函数。
如果还是觉得不过隐,那我们就继续,下图展示了刚才那段C#代码的IL(用ILDasm反汇编即可):
在C#中委托是作为一个特殊的类型(Type,Object)来对待的,委托对象也有自己的成员:
BeginInvoke,EndInvoke,Invoke。
这几个成员是你定义一个委托时编译器帮你自动自成的,而且他们都是virtual函数,具体函数体由runtime来实现。
我们双击一个callback,可以看见以下IL:
//代码大小
9(0x9)
.maxstack
8
IL_0000:
ldarg.0
IL_0001:
ldarg.1
IL_0002:
ldarg.2
IL_0003:
callvirt
instancevoidClass1.ExcelProgram/myFun:
:
Invoke(int32,
int32)
IL_0008:
ret
}//endofmethodExcelProgram:
callback
从这段IL我们可以看出,当我们使用语句:
fun(a,b)时,调用的却是委托对象(即然委托是类型,那么他自也就会有对象)的myFun:
Invoke().该委托对象(即上面的mulCast)通过调用Invoke来调用对象本身所关系的函数引用。
那我们再看看,一个委托对象是怎么样关联到函数的呢,我们双击Main函数,可以看到以下IL,虽然IL语法复杂但仍不影响我们了解它是怎么样将一个委托关联到一个(或多个)函数的引用的。
.methodprivatehidebysigstaticvoid
Main(string[]args)cilmanaged
.entrypoint
.custominstancevoid[mscorlib]System.STAThreadAttribute:
.ctor()=(01000000)
58(0x3a)
4
.locals([0]int32i,
[1]int32j,
[2]
classClass1.ExcelProgram/myFun
mulCast)
ldc.i4.s
10
stloc.0
55
IL_0005:
stloc.1
IL_0006:
ldnull
IL_0007:
ldftn
voidClass1.ExcelProgram:
max(int32,
IL_000d:
newobj
.ctor(object,
nativeint)
IL_0012:
stloc.2
IL_0013:
ldloc.2
IL_0014:
IL_0015:
min(int32,
IL_001b:
IL_0020:
call
class[mscorlib]System.Delegate[mscorlib]System.Delegate:
Combine(class[mscorlib]System.Delegate,
class[mscorlib]System.Delegate)
IL_0025:
castclass
Class1.ExcelProgram/myFun
IL_002a:
stloc.2
IL_002b:
IL_002c:
ldloc.0
IL_002d:
ldloc.1
IL_002e:
callback(classClass1.ExcelProgram/myFun,
int32,
IL_0033:
string[mscorlib]System.Console:
ReadLine()
IL_0038:
pop
IL_0039:
Main
从上面的IL可以看出对于语句:
myFunmulCast=newmyFun(max);
是通过以max作为参数构建一个委托对象mulCast。
但对于语句:
mulCast+=newmyFun(min);
等价于(你甚至可以用下面的语句代码上面的:
mulCast+=newmyFun(min)):
mulCast=(myFun)Delegate.Combine(mulCast,newmyFun(min));
哦,原来是通过调用Delegate.Combine的静态方法将mulCast和min函数进行关联,Delegate.Combine方法只是简单地将min函数的引用加至委托对象mulCast的函数引用列表中。
4,Introduceevent;
事件/消息机制是Windows的核心,其实提供事件功能的却是函数指针,你信么?
接下来我们再看看C#事件(Event).在C#中事件是一类特殊的委托.
一个类提供了"
事件"
,那么他至少提供了以下字段/方法:
一个委托类型的字段(field),用来保存一旦事件时通知哪些对象。
即通知所有订阅该事件的对象.别忘记C#中委托是支持多播的。
两个方法,以委托类型为参数。
作用是将订阅该事件的对象方法加至上面的委托类型字段中,以便事件发生后可以通过调用该方法来通知对象事件已发生。
我们简单地定义一个类Test,该类支持事件:
classTest
publiceventEventHandlerOnClick;
publicvoidGenEvent(EventArgse)
//引发事件方法
EventHandlertemp=OnClick;
//通知所有已订阅事件的对象
if(temp!
=null)
temp(this,e);
我们反汇编这段代码,如下图:
简单地定义一个字段哪来的那么多方法?
其实这都是编译器帮你加上去的。
当你定义一个事件时,编译器为了实现事件的功能会自动加上两个方法来提供“订阅”和“取消订阅”的功能。
通过下面的语法,你便可以订阅事件:
test.OnClick+=newEventHandler(test_OnClick);
也就是说,一旦test事件发生时(通过调用test.GenEvent()方法)。
test便会调用注册到OnClick上的方法。
来通知所有订阅该事件的对象。
订阅是什么?
“订阅就是调用定义事件时自动生成的add_OnClick.”“那取消订阅就是调用定义事件时自动生成的remove_OnClick”,恭喜你!
都学会抢答了.对于上面的订阅事件语句,逻辑意义上等同于:
test.add_OnClick(newEventHandler(test_OnClick));
但C#并不能直接调用该方法,只能通过"
来实现。
来看IL:
IL_003b:
test_OnClick(object,
class[mscorlib]System.EventArgs)
//先将test_OnClick压栈
IL_0041:
instancevoid[mscorlib]System.EventHandler:
nativeint)
//new一个委托对对象
IL_0046:
callvirt
instancevoidClass1.ExcelProgram/Test:
add_OnClick(class[mscorlib]System.EventHandler)
//通过调用add_OnClick方法将上面生委托加至test的事件(委托列表)中.
5,summarize.
如果对设计模式中的观察者模式较为熟悉的话。
其实支持事件的类也就是观察者模式中的Subject(主题,我个人比较喜欢这么译).而所有订阅事件的对象构成了Observers.
最后来句总结吧,总结也许不严谨,但提供理解那还是绝佳滴..我骗你..(鼻子又变长了).....
"
是"
函数指针"
链表,当然该链表也可以只有一个元素,如果这样的话:
约等于
;
是一类特特殊的"
,你定义一个"
表示你同时定义了:
一个委托+两个方法。
后记:
如果还不理解事件,先不要急,说不定你先把它忘记不想,等会一闪光,你就会理解了。
或者你等着我下一篇《设计模式----观察者(Observer)》,我想等你看完设计模式中的观察者之后再回来看"
,看"
多播委托(MulticastDelegate)"
应该可以:
忽然开朗。
如果还觉得不过隐。
下面给出一个很好的帮助理解的例子,来自JeffreyRichter.希望我的注解能帮上些忙:
System.Text;
System.Data;
//定义事件引发时,需要传的参数
NewMailEventArgs:
EventArgs
private
readonly
string
m_from;
m_to;
m_subject;
public
NewMailEventArgs(string
from,
to,
subject)
m_from
from;
m_to
to;
m_subject
subject;
From
get{return
To
Subject
//事件所用的委托(链表)
NewMailEventHandler(object
sender,
NewMailEventArgse);
//提供事件的类
MailManager
event
NewMailEventHandlerNewMail;
//通知已订阅事件的对象
protected
virtual
OnNewMail(NewMailEventArgse)
NewMailEventHandlertemp
NewMail;
//MulticastDelegate一个委托链表
if(temp
!
null)
//通过事件NewMail(一种特殊的委托)逐一回调客户端的方法
//提供一个方法,引发事件
SimulateNewMail(string
NewMailEventA