形象解释回调函数的文章
上一篇 / 下一篇 2007-09-20 09:44:32 / 个人分类:软件开发
#k4{9F M7qU&A0 测试的软件中很多地方用到了回调函数,而对于我这个对开发一知半解的人来说,理解这个概念费了不少劲。今天总算在网上发现一篇文章,对回调函数的解释比较形象,现转贴出来(不过这个网址也是转的):51Testing软件测试网JR$lgiP
51Testing软件测试网U0Fn ? M4V |~+G出处:http://blog.csdn.net/nine425/archive/2007/02/10/1506982.aspx 51Testing软件测试网3N!t%Me1iD3Li f
51Testing软件测试网 K,g]&j7R Z2v q内容:关于回调函数51Testing软件测试网'{NB,C$z5@,I
7?&H(z-rJo8|i_5d8T0Q: 帮忙回答下面几个问题。谢谢,我对C++不熟,所以回答的问题请尽量避免C++程序。
CyD%mzIm051Testing软件测试网.Sv#\Ek0Iu
1)在编程过程中哪些程序是必须用回调函数才能解决的或者说用回调函数解决更好?51Testing软件测试网-esBy/OX ~8k2X
请尽量说细些使用回调函数的原因。
3KD2}S&B051Testing软件测试网sE0qpg"[v8r?
2)我看网上一些例子.NET中一般用回调函数都会涉及到委托,那么委托和回调函数是什么关系?
0H MXx
wrVn/N9W0 为什么要用委托。51Testing软件测试网t ?*A4d3S
51Testing软件测试网SFQng)q o9w
3)如果可以不用委托的话,那么用了委托有什么好处?
X5^9{EC%EU051Testing软件测试网6Mh2D%M7i Q:X*H
51Testing软件测试网 Rx![:F%u"[!I
51Testing软件测试网:eaV1X D
D&S1I+eS
A: 关于委托、事件以及一点点异步处理的内容,可以看看我这里的回帖
p%c"EvfE051Testing软件测试网SI
P9aK'F4Et
(上一篇)
:?.v4Sk'vP+wGzM*s*T0
@ ]h8X9F
I2D9Jy%}0---------51Testing软件测试网H-[$[Y%om
/K&? h(yV*D0现在就你的问题将回调函数和异步处理详细说说。51Testing软件测试网dHJP
y'f3}
51Testing软件测试网#Y-C|'K [kR
如果你OOP编程经验足够的话,你就会有这么一个印象:对象一般来说都是被动的,外界可以通过调用它的方法,或者访问它的属性来告诉它发生了什么事情、需要怎么处理,但一般情况下对象不会主动去观察不属于它管辖范围的外界的事情。
&t~$v#p(K+A051Testing软件测试网!M6Nd"ER`+P|h'p
但是有时候的确需要有一个机制,让它可以了解到不属于它管辖范围的外界的事情。虽然可以通过无限循环观察达到这个目的,但是当然这将耗费很多资源。按照上一段所说的原则,对象最好就保持被动,由能管外界的其他对象主动告诉它发生了什么事情。51Testing软件测试网}(EM/Mz
51Testing软件测试网bZ0l~)lQ
一个生活化的例子:主管(一个对象)叫一个下属(另一个对象)打一份文件。主管需要知道下属什么时候打完,但是如果守在下属旁边等待工作完成,当然是很愚蠢很浪费自己时间的事情。所以,主管可以下达一个命令:“你打完了就告诉我。”这样,主管就可以做自己要做的工作,并在下属打文件这个过程中保持被动,由下属负责主动通知主管他打完文件。51Testing软件测试网]xs~9X2S_D
DMb Isu;q
T0于是就出现“回调函数”的概念,用生活化的话来说,就是“当发生什么什么事情的时候,告诉我”这样的概念(回调=callback=反向的调用,不是自己调用其他对象的方法,而是让其他对象反过来调用我的方法)。使用的场合很多,但是由于名字的限制,一般都只狭义地指异步处理方法完成之后用来通知调用者的那个方法。
zc.^ n*{u4s ?)Vw051Testing软件测试网9iT:I9gi[
w
来看一个异步处理的例子:向网络流写一个字节数组。
e+R0`$aLPng(q051Testing软件测试网dP0_t t
NetworkStream.BeginWrite(byte[] data, int offset, int count,
$Y+K9k#S@0 --> AsyncCallback callback <-- , object state);
Q4Q6OiX:r051Testing软件测试网jtU&E
hBG
与一般的写(Write(byte[], int, int))不同的地方是多了个回调函数参数和回调函数里可以使用的自己指定的任意对象。人性化之后,这个方法的意思就是“麻烦将这个数组里面从offset开始的count个字节发出去。做完之后告诉我,并且记得告诉我你在做哪个东西免得让我混淆”。51Testing软件测试网6@3s { i
T}f#E
51Testing软件测试网T`9G7c Rp
网络流发送完要发送的内容之后,它就会调用这个作为参数传入的回调函数,并且将你提供的state对象原封不动交回给你。然后你调用 NetworkStream.EndWrite(...) 来正式结束这个异步处理过程。
0{FE]aP*N}O1Z3_051Testing软件测试网`T9Y5X+t HbE
除了异步处理,也有一些例外的情况会使用到“回调函数”,但是其实可以简单的理解为利用回调函数的方便性来达到不算回调的目的。例如在线程池中添加一个工作:51Testing软件测试网lf
le4B
51Testing软件测试网})JJ"^ |r"Lc
ThreadPool.QueueUserWorkItem( --> WaitCallback callback <-- , object state);51Testing软件测试网\%[gwhRJ
P_2d0s/h0它的意思是“有线程空闲的时候,麻烦运行这个函数。”严格来说这不算“回调”,因为指定的函数可能是在别的对象上的方法,但是因为回调函数的概念“好用”,所以这里“滥用”了一下。
vT]/UfLb_ \0
,B'^!wb9?V;d0更加滥用的情况其实逻辑上也是可以的,因为回调函数其实就是委托。委托其实就是一个方法(函数)的“包装”,它可以将一个特征符合的方法包装成一个变量,变量传到哪里,哪里就能通过它运行所包装的方法。(解释“特征”:英文叫signature,中文翻译用了signature的另一个翻译——“签名”,但是意思是“特征”,指方法的参数列表和返回值。如果两个方法有相同的参数列表(参数名可以不同)和返回值,它们就有共同的特征)
Et^ l5B
Mj'K~5U0
X
M g\]x0委托的使用场合我在本回复开头的连接那里说过了,就是需要调用一个方法的时候,但是不知道具体该调用哪个对象的哪个方法的时候。用委托包装方法之后,“调用方法”就可以代替为“调用委托”,无须知道方法来自哪里。51Testing软件测试网v0W4EfR9K6s
51Testing软件测试网/V%s\
D,A
如果完全不用委托,理论上勉强能解决现在所有用了委托的情况,但是程序将变得非常复杂。就用网络流异步写数据的例子,如果不用委托的话,我猜大概可以这样:51Testing软件测试网V2N+gAu*lx1I
51Testing软件测试网/K\Izs$Bsj!d\
---<虚构>---
Shz
@!\2}0调用者实现 IStreamWriteAsyncCallbackHandler 接口(流的写操作的异步回调处理者),该接口有一个 StreamWriteAsyncCallback 方法,当流完成写操作之后,找到它的调用者,类型转换为这个接口,然后调用这个回调方法:51Testing软件测试网/b ['SX3q_d7`
((IStreamWriteAsyncCallbackHandler)caller).StreamWriteAsyncCallback(asyncResult);51Testing软件测试网,p'oL2?\:s4GSh`,GR!I
---</虚构>---51Testing软件测试网 a#dG4tW3}
51Testing软件测试网wmt;|y5OVO8r
复杂度可见一斑。
(O3Xgf\6f051Testing软件测试网K,SO0yP+Xg({I
有什么问题再提吧,我说得够多了