精灵王
- 注册日期2010-12-08
- 发帖数640
- QQ
- 火币1103枚
- 粉丝120
- 关注75
|
阅读:3581回复:0
对c#下函数,委托,事件的一点理解!_c#应用
楼主#
更多
发布于:2011-01-08 21:15
| | | | 今天一来是有点空,二来是在博客上偶然看到有关于委托的文章,一时兴起,就自己也写一点心得和大家分享一下。先看一个例子:using System; namespace ConsoleApplication1 { class Class1 { [STAThread] static void Main(string[] args) { bool m_isRight = false; object m_obj = m_isRight?MyWrite("true"):MyWrite("false"); Console.Write(m_obj); } static private int MyWrite(object i_string) { Console.Write(i_string); return i_string.ToString().Length; } } }问输出的结果是什么?有一个刚学习程式设计不久的学生的回答是:false false这个结果给我的映像非常深,为什么呢?因为我觉得这个不仅仅是学生的一个错误,而更多的是这个学生深入的思考了问题。因为m_obj是个对象,所以这个学生理解为:MyWrite()这个函数对象能直接赋值给m_obj,然后m_obj就当成MyWrite()这个函数来调用,所以他就认为:Console.Write (m_obj); 等于是:Console.Write (MyWrite(“false”)); 这是思维是非常有创意的,不是吗?于是就是C#里而非常多人不好理解的委托了。其实,从使用上讲,他就是个函数变量!如上面的例子,如果真的是想把MyWrite()做为对象赋值给m_obj会是个什么结果呢?我觉得我们先得解决以下几个问题,才能正确的把函数当成变量赋值给一个对象:1、如果能给一个对象赋函数值,怎么来差别不同的函数?2、怎么差别他是个函数赋值,还是个普通的对象赋值?3、怎么用这个对象来调用原来的函数?如果把这几个问题解决了,委托也就明白了一半。先看问题1,如果能给一个对象赋函数值,怎么来差别不同的函数?首先应该明白的是:C#里是能对一个对象赋函数值的。解决这个问题的办法是先对该对象申明,申明他能被什么样的函数来赋值,而这个对象申明在C#里的学名就是委托。(在C++里称为函数指针申明,相应的对象也就叫做函数指针。java里也不同的叫法,可惜我不知道。)而他的语法就是:delegate [function declare];这里的function declare就包括了:1、函数返回类型,2、可用来存放函数的对象名(也就是委托名)3、函数参数所以完整的定义能是:delegate int MyDelegate(object I_object);当然,合法的委托定义能是:delegate void MyDelegate();delegate void MyDelegate(object I_1,object I_2);…目前,上面的语法就定义了一个抽象的对象MyDelegate, 注意,这里说的是抽象的对象,也就是说,你不能直接给MyDelegate赋函数,而只能在他的实例上函数,这是C#里特别的需求。他的语法是:MyDelegate m_delegate = new MyDelegate(和MyDelegate申明一致的函数名);例如,以下是个完全的,合法的委托申明和实例化一个对象:delegate int MyDelegate(object i_object); // MyDelegate m_delegate = new MyDelegate(MyWrite); //MyWrite函数如下,他是满足委托的申明的。 static private int MyWrite(object i_string) { Console.Write(i_string); return i_string.ToString().Length; } 目前我们就非常好的解决了第一个问题,怎么定义一个对象,使该对象能把函数当变量来赋给他。而且,能差别不同的函数类型,主要是通过函数返回值和函数参数来共差别一类函数。OK,第二个问题:如果有了这样的一个对象后,怎么来给他赋一个函数值呢?其实上面的实例化一个委托对象时,就已给他赋值了。上面的代码中,m_delegate就已被赋值MyWrite,因此他已具有了MyWrite函数的功能。更有其实他的方法来给他赋值吗?有,在委托的一个应用中,能看到其他的赋值方法。也就是另一个不好理解的概念:事件!后面会提到。我们再来看一下最后一个问题:怎么通过一个已赋值好了的委托对象,还调用他上面赋值了的函数。这个最简单了,当一个委托实例赋了函数对象在上面后,就能像调用原函数相同的来调用他了。因此,下面是个会法的调用:基于上面的申明。m_delegate(“This is a delegate object to call the raw function.”);他就等同于:MyWrite(“This is a delegate object to call the raw function.”);因此,上面的调用和原函数调用相同,会返回一个int结果。OK,最后看一个完整的例子:using System; namespace ConsoleApplication1 { class Class1 { //先申明一个委托对象。 delegate int MyDelegate(object i_object); [STAThread] static void Main(string[] args) { MyDelegate m_delegate = new MyDelegate(MyWrite); m_delegate("This is a delegate object to call the raw function."); } //该函数是满足上面委托对象的申明的。 static private int MyWrite(object i_string) { Console.Write(i_string); return i_string.ToString().Length; } } }再来讨论一下他的应用:事件!事件是其于委托的。我们还是先来看最开始的那个例子:object m_obj = m_isRight?MyWrite("true"):MyWrite("false");我想把一个函数对象赋值到m_obj上!但上面的委托只能在实例化对象的时候就直接给他赋值了。而目前是,在运行时对一个委托赋函数值。能做到吗?同样是有这样的向个问题,当然,前提是我们已知道有一种对象叫委托,他的实例能赋函数对象。下面的问题是:1、如果能在运行时给某个“特别委托”赋函数对象,怎么实现?2、运行时,怎么知道该“特别委托”是否已被赋过函数值?及怎么再赋值?3、如果能,能否在一个“特别委托”上添加多个函数?如果能,怎么删除函数?下面,我们就针对这几个问题,来讨论一下C#里的事件,也就是上面的“特别委托”。(其他语言里是怎么实现这些功能的,我就不清晰了。)首先,C#里是能实目前运行时给一个委托动态的赋函数值的,同时也是能动态的删除已添加在某个委托上的函数的,他的实现有一点点麻烦,就是要用到另一个对象:事件!event(申明,你完万能不把他叫事件,只不过这种动态的添加和删除函数的功能在真实的程式设计中,基本上是和事件相关,所以就叫做事件了。个人想法,呵呵。)OK,下面是C#语法,来申明一个“特别委托”――事件,让他能动态的添加函数!下文中,事件是指那些“特别委托”,他的特别之外,后面会讲到。而下文中的委托就是前面讲到的,一个特别的对象,该对象能把函数当“值”赋给他。static event MyDelegate m_myevent;(static 能用其他的修饰符)说明一下,这里其实就是申明了一个事件,用event来说明他是事件(特别委托)的。其实对比实例化一个委托的语法,你能理解到,他就像是申明了一个委托,只不过个委托加了个event来说明他:Mydelegate m_delegate;//申明一个委托event MyDelegate m_myevent;//申明一个事件非常像吧!不是吗?OK,我们再来看,m_myevent 和m_delegate到底有什么不同的?也就是,事件(特别委托)到底特别在什么地方?1、事件不是个能直接把函数当值相同赋给他的委托。而委托能直接赋函数,而且是在实例化的时候,赋函数名。2、事件只能把一个实例的委托当值赋给他。也就是说:事件是用来管理委托的,进而来管理函数!因为一个实例化的委托一定有一个函数和之对应。3、在事件上能动态的添加和删除委托。而委托上不能动态的添加删除函数。OK,下面的一个问题,上面事件的申明中,MyDelegate是起什么作用的呢?还记得前面的委托申明吗?他就是说明了m_myevent在运行时能动态的以委托的形式赋的函数要和MyDelegate申明的相同!因此上面的一个实例化是完全合法的。再理解一下:事件,是用来动态管理委托的,而委托是单一的和一个函数对应的。 目前看第二个问题,运行时,怎么知道该“特别委托”是否已被赋过函数值?及怎么再赋值?即:怎么知道一个事件上已赋过经过委托过的函数?前面已说过,m_myevent没有给他赋值,怎么给他赋值呢?他的赋值方法有点怪:一个实例:m_myevent += m_delegate;有点怪吧!这里正好说明了:事件是用来动态管理委托的。把一个委托加在事件上。当然,你还能在添加委托的时候直接new一个新的委托:m_myevent +=new MyDelegate(Class1_m_myevent);//后面的函数名由vs2003生动生成这就是.net下标准的给事件添加委托的方法,也就是给一个事件添加了一个可调用的函数。确切的说,是个回调函数(C++的概念)。 OK,下面就怎么判断一个事件上是否已被赋过经过委托的函数:if(m_myevent==null)就能知道了!那么怎么知道一个事件上面有多少个委托呢?也就是多少个委托过的函数?m_ myevent.GetInvocationList();能得到所有的委托!这里应该知道:事件本身是个对象,当实例化一个事件后,他是有自己的一些方法也成员的。能查阅MSDN得到更多说明,同样的委托也是个对象,相关的说明也能在MSDN里找到。最后的问题:怎么删除一个已添加在事件上的委托?太容易而且太有意思了:m_myevent -= m_delegate;那么这样的几个问题又来了:1.如果这个事件上没有该委托,“减掉”以后会出错吗?不会,放心的减吧。2.怎么调用这个事件上的委托呢?上面有多个委托,他是怎样运行呢?调用事件上的委托和调用委托上的函数是完全相同的:你要给出和委托申明相同的函数参数,并且调用会返回和申明相同的数据类型。最后再回来看这个问题:object m_obj = m_isRight?MyWrite("true"):MyWrite("false");怎么解决他呢?把一个函数赋给一个对象:一个例示(仅做演示解决这个问题,个人认为这样的做法没有实用意义) using System; namespace ConsoleApplication1 { public class Class4 { event System.EventHandler m_obj; public Class4() { System.EventHandler m_f1 = new EventHandler(SomeFunc1); System.EventHandler m_f2 = new EventHandler(SomeFunc2); bool m_someCondition = false; m_obj += m_someCondition?m_f1:m_f1; m_obj(this,null); } private void SomeFunc1(object sender, EventArgs args) { } private void SomeFunc2(object sender, EventArgs args) { } } }最后看一个完整的例子:using System; namespace ConsoleApplication1 { class Class1 { //先申明一个委托对象。 delegate int MyDelegate(object i_object); static event MyDelegate m_myevent; [STAThread] static void Main(string[] args) { MyDelegate m_delegate = new MyDelegate(MyWrite); m_delegate("This is a delegate object to call the raw function."); m_myevent += m_delegate; m_myevent += new MyDelegate(MyWrite); m_myevent +=new MyDelegate(Class1_m_myevent); if(m_myevent!=null) { m_myevent("This is a event to call the funcaion on the delegate."); } } //该函数是满足上面委托对象的申明的。 static private int MyWrite(object i_string) { Console.WriteLine(i_string); return i_string.ToString().Length; } private static int Class1_m_myevent(object i_object) { Console.WriteLine(i_object); return 0; } } } 我们再来看一个.net下标准的事件驱动模型的例子:using System;namespace ConsoleApplication1 { public delegate void MyEventHandle(object i_sender,object i_arg); public class Class2 { public Class2() { } [STAThread] static void Main2(string[] args) { Class3 m_runOject = new Class3(); m_runOject.OnError += new MyEventHandle(m_runOject_OnError); m_runOject.OnSomeThingHappened += new MyEventHandle(m_runOject_OnSomeThingHappened); m_runOject.Run(); } private static void m_runOject_OnError(object i_sender, object i_arg) { Console.WriteLine("Error in {0}, arg:{1}",i_sender,i_arg); Console.WriteLine("Object {0} will stop running.",i_sender); (i_sender as Class3).Stop(); } private static void m_runOject_OnSomeThingHappened(object i_sender, object i_arg) { Console.WriteLine("Something happended in {0}, arg:{1}",i_sender,i_arg); } } public class Class3 { public bool m_isStop = false; public event MyEventHandle OnSomeThingHappened; public event MyEventHandle OnError; public Class3() { } public void Run() { Random m_rand = new Random(); int m_randomNum = m_rand.Next(); while(!m_isStop) { if(m_isStop){break;} m_randomNum = m_rand.Next(100); if(m_randomNum%5==0) { if(this.OnError!=null) { this.OnError(this,m_randomNum); } } else { if(this.OnSomeThingHappened!=null) { this.OnSomeThingHappened(this,m_randomNum); } } } } public void Stop() { m_isStop = true; } } }好了,全部完了! 最后再从另一个角度来理解:函数,委托和事件!1、函数,是程式的基本单元,在.net下,有一个非常重要的思想,就是:一切对象化!你可把一个int boxing后得到一个object,也能把一个object unboxing后得到一个int. 可惜,就是没有函数对象化这个概念!?2、其实函数也能对象化!就是通过delegate,委托就是把函数作为对象来处理,使他函数也具有了对象的一些特点。在实例化一个委托的时候,就是把一个函数”boxing”。因此,委托本质上就是个类!他的初始化参数是个函数名!不同的委托是不同的类,对应不同类型的函数。3、事件是对委托的一个管理封装,他能非常好的动态管理委托,从而完成非常多有实用价值的事情,最主要的就是事件! 更多黑客技术 黑客软件 计算机技术 编程技术 网站技术 qq技术 IT新闻 黑客基地 请访问 灯火安全联盟 灯火黑客 www.hack8888.com/bbs
| | | | |
|