解C#中的代理和事件(一)
突然写这篇文章,真的有点,是在做作的感觉,我想这并不是什么,难以
理解的东西,事实上,很多人都写过,而且,我保证至少不比我写的差.可是
还是觉得有必要提出来.因为要去正确的理解代理和事件是很有趣的,也是
很必要的.那么好吧,下面我就来讲讲,它们之间的关系.当然还会有些例子.
首先我想说说有关事件.事件顾名思义当然是windows程序收到的消息.
那么我举几个有关事件的例子,如鼠标移动.按下.之类的都是.那么代理呢?
很多人都说它看上去,就是想是一个受托管的函数指针.我觉得这种说法很
正确,事实如此.在MSDN上,我们经常可以看到,有关代理的"多路广播"这个词
我觉得不错,可是不好理解.但我会在下面详细讲解的.
代理:(有的书上也翻译成指代或委托.英文是这样一来的"Delegate")
我想很多刚接触C#的人,都会对代理产生兴趣的.事实上也是如此,不了解它,你就没办法
来做windows程序.和传统意义上的函数指针有所不同的是,代理在C#中是一种类型,这样
它看上去,更安全也,更符合OO精神.代理实际上所做的工作就是通过引用把函数包裹起来
并使函数有一个有效的返回值.不知道我这样说是否好理解.那么我举个例子,你去建造房子.
很显然,我是在说你所做的事情.那么建造房子就是代理,它指代了,你要做的事情,可是它并
没有去做任何事情,事实上是,在建造房子这个工作里,你做了,那么结果是什么?当然是建立
一座房子.是的,建造房子就是代理,而如何建造房子则是函数应该完成的工作.而建造的是什么样
的房子,则是返回值.还记得,我曾经说过,代理是一种类型吗?呵呵..我想你应该记得,因为,
那是很新颖的,至少当时我那么认为.好吧,让我们来看看名称空间System.Delagate,看见了吗?那
就代理类.
举个例子: public delegate void GetString()//我申明了一个代理
现在我要用到它了如下; int i=100; GetString gs=new GetString(i.ToString);//这里我吧int的ToString方法
填入了一个代理中.看上去想构造函数.这就是常在书上看到的"名称等效的,而
不是结构等效的",我想看到这儿你还是不明白.那么,我再来一个代理
如下:
float j=0.0001;
GetString gs=new GetString(j.ToString);//瞧见了,int的ToString方法
和float的ToString方法的结构是不一样的,可是名称和类型的返回值和
参数都一样.现在,我想,你应该理解了吧.
可是,我们经常会在MSDN中看到,这样的句子.单路代理和多路广播.看上去,有点不好理解.
事实上,我开始看这样的句子,是有点头痛的.那么,我想例子是最好的解说方式.
Single Delegate:(单路代理)
从字面上,我们可以这样来理解,这个代理只是单单代理了一个函数的工作.那么好吧,让
我们来看看它是如何工作的.下面我就来定义一个这样的代理:
public delegate bool Myfun(string str,int i)
现在我再来写一个方法如下: bool CompareStrToInt(string s,int i) { if(s.CompareTo(i.ToString())==0) return true; else return false; }
这个方法完成的工作很简单对吧,只是比较字符而已.那么和代理有什么关系呢?还记得
我说的话吗?代理就是在把动词名词化.代码如下:
Myfun mf=new (CompareStrToInt);
string s="10000"; int i=10000; ConSole.WriteLine("Value="+mf(s,i));
输出结果:
Value=true
这就是单路代理.它只代理一个.好吧,也许你想看看复杂的例子,更有趣的在后面呢,
该是讨论多路广播的时候了.
多路广播:
一个代理同时代理几个方法.就是我们前面说到的那样,你去建造房子,现在要不仅仅是
建造住宅,还的去建造花园等等其它建筑物.可是它们都是在建造房子,传递的参数也相同
返回值的类型也相同都是房屋.那么我们为什么不找一个代理人来完成这样的任务呢?把
这些事物交由他一个人来完成不是可以节省我们很多的时间和金钱.是的我们可以那样做
System.MulticastDelegate 实际上在.net framework中你还可以找到这个类,多路代理
MSDN上翻译成多路广播.事实上它还重载了操作符+=.其实多路广播和单路代理在使用方法
上区别不大.你可以看下面的例子.
using System;
namespace Multi_castDelegate { /// <summary> /// Summary description for Class1. /// </summary> class MyClassDelegate { /// <summary> /// The main entry point for the application. /// </summary> public delegate string IntDelegate(string s); } } using System;
namespace Multi_castDelegate { /// <summary> /// Summary description for MyImplementingClass. /// </summary> public class MyClass { public MyClass() { }
public static string WriteString(string s) { Console.WriteLine("Writing string"); return "null"; }
public static string logString(string s) { Console.WriteLine("loging string"); return "null"; }
public static string TransmitString(string s) { Console.WriteLine("Transmitting string"); return "null"; } } }
The Main class: using System; using System.Threading; namespace Multi_castDelegate { /// <summary> /// Summary description for Test. /// </summary> public class Test { public static void Main() { MyClassDelegate.StringDelegate Writer,Logger,Transmitter;
MyClassDelegate.StringDelegate myDelegate;
Writer=new MyClassDelegate.StringDelegate(MyClass.WriteString); /// calling Writer Writer("hello i am Writer just acting like Single cast"); Logger=new MyClassDelegate.StringDelegate(MyClass.logString); ///calling Logger Logger("hello i am Logger just acting like Single-cast"); Transmitter=new MyClassDelegate.StringDelegate(MyClass.TransmitString); ///calling Transmitter Transmitter("hello i am Transmitter just acting like Single-cast"); ///here mydelegate used the Combine method of System.MulticastDelegate ///and the delegates combine myDelegate=(MyClassDelegate.StringDelegate)System.Delegate.Combine(Writer,Logger); myDelegate("used Combine"); ///here Transmitter is also added using the overloaded form of Combine myDelegate+=Transmitter; myDelegate("Using Overloaded Form"); ///now using the Remove method myDelegate=(MyClassDelegate.StringDelegate)System.Delegate.Remove(myDelegate,Writer); myDelegate("Without Writer"); ///overloaded Remove myDelegate-=Transmitter; myDelegate("Without Transmitter"); System.Threading.Thread.Sleep(2300); } } }
(上面的例子是在一个国外网站上找到的,觉得不错,就直接套用了.)
上面的例子重点是看那两个已经重载的操作符."-="和"+=".通过上面的例子,你可以清楚的
看到多路广播是如何一次代理多个方法的.当然你也可以删除掉那些你不想要的用"-="操作
符就可以了.(那么我将在下一篇讨论事件)
嗨!大家好,还记得刚才的那篇关于代理的吗?你是不是要问我为什么要分开来写?
原因很简单他们说要7篇以上才可以成为专题作家.噢,天啊,事实上这是我的第九篇
可是那两篇.还在审核中.开玩笑的,事实上是,结构,代理和事件有联系,可是也是可以
分开来讨论的两个话题.这就是我为什么要分开,因为,很多朋友一开始用到代理和事件
的时候,都是在给一个button或一个窗体添加事件的时候用到的.所以经常可以看到,有
人一提到代理和事件就会自然想到windows的时间驱动.是的那是最常用的,可是事实上
也是可以分开使用的,至少代理就是那样的.代理可以脱离事件来独立生存.上面的那一篇
正如你看到的那样.好吧,不多说了,让我们进入正题.
事件:
理解事件,这一点也不难,事实上,每个人每天都在经历着这样或那样的事件,然后组成了,你
的生活.想想,一个天气预报员通知你说明天要下雨了.很自然的,你会想到,你该代把伞.是的
这就是事件.天气预报发出这个事件通知,然后你来处理这个事件.你所做的就是带把伞来挡雨.
就象你常做的那样,你点击一个button然后发出click事件,接下来当然是处理这个事件了,也许是
弹出一个窗口,或别的什么事情这些都有可能发生,甚至是关闭计算机.这些都是用户事件.因为用户
触发GUI然后发生这些事件.
下面我们来看看C#中是如何处理的.那么我前面的那篇中曾提到过,把动词变成名词.你会想到什么?
是+ing吗?哈哈..开个玩笑,当然不是,我们不是在做英文联系.好吧,是代理,就象你常做的那样
在IDE环境中,你拖一个button到窗体上,然后双击它,你会看到什么?进入代码界面了吗?然后你就
来处理事件.可这和代理有什么关系?恩,你为什么不仔细找找看通常有怎么
this.button1.Click += new System.EventHandler(this.button1_Click);一句在你的 InitializeComponent()函数中.是的,这就是代理.它把事件放入了代理中.那么事件呢?
在this.button1_Click方法中你会看到它有两个参数,一个是object类型的sender,这个很简单
顾名思义它就是事件的发起人.它是谁,你只要问问它就知道了.那么第二个呢?是事件,对吗?
当然,发起人带着他的事件来了,就象天气预报员带着他的天气预报来了一样.好了,相信我
人类总有一天可以制造天气.听着一定要相信我,出了打雷下雨等这些上帝已经预定好的天气
我们总有一天会创造出新的天气效果例如,下钱怎么样?呵呵..听上去是个不错的想法,虽然是
个玩笑.好的,你会说我准是疯了,干嘛说这些?不,我要谈到一个新的话题.那就是用户自定义
事件和系统预定义事件.那么下面我们来分别讨论这两种事件.
用户自定义事件:
我相信代码永远都是最有说服力的.就象你不愿意去看教材而更愿意去看科教电影那样.
下面的代码,就是我要说明的事实.
using System; using System.Drawing; using System.Collections; using System.ComponentModel; using System.Windows.Forms; using System.Data;
namespace userEvent { /// <summary> /// Form1 的摘要说明。 /// </summary> public delegate void MYShow();
public class Form1 : System.Windows.Forms.Form { /// <summary> /// 必需的设计器变量。 /// </summary> private System.ComponentModel.Container components = null; public event MYShow ShowME; public Form1() { // // Windows 窗体设计器支持所必需的 // InitializeComponent();
// // TODO: 在 InitializeComponent 调用后添加任何构造函数代码 // this.ShowME(); }
/// <summary> /// 清理所有正在使用的资源。 /// </summary> protected override void Dispose( bool disposing ) { if( disposing ) { if (components != null) { components.Dispose(); } } base.Dispose( disposing ); }
#region Windows Form Designer generated code /// <summary> /// 设计器支持所需的方法 - 不要使用代码编辑器修改 /// 此方法的内容。 /// </summary> private void InitializeComponent() { // // Form1 // this.AutoScaleBaseSize = new System.Drawing.Size(6, 14); this.ClientSize = new System.Drawing.Size(292, 273); this.Name = "Form1"; this.Text = "Form1"; this.ShowME+=new MYShow(this.ShowMeName); } #endregion
/// <summary> /// 应用程序的主入口点。 /// </summary> [STAThread] static void Main() { Application.Run(new Form1()); } void ShowMeName() { MessageBox.Show("I'm yarshray"); } } }
看见上面的代码了吗?不用我多说,你也猜到我要说的是什么,看我自定义的一个代理
public delegate void MYShow(); 这个代理将要代理一个事件,它是在这儿定义的
如下:public event MYShow ShowME;我定义的这个事件的名字叫做ShowME是的,人如其名
就是要展示一下我,呵呵..,看看吧,下面的一句this.ShowME+=new MYShow(this.ShowMeName);
我使用了这个事件并把ShowMeName交给了他.这个方法很简单就是弹出一个对话框.写上我
的名字"yarshray",可是这这个事件是在那儿触发的呢?为什么不向上看?看见构造函数了吗?
是的,里面有一句 this.ShowME();,可是你知道,这样做没有任何意义,实际情况是需要自定义
消息然后把它掉到消息循环中,然后通过消息来触发事件.不过我只是在讲有关代理和事件,所以
并没有那么做.只为了图简便.让你能够看的更清楚.好了,用户自定义事件我就讲到这里.下面的
时间让我们来讲讲预定义事件.
预定义事件:
实际上不用我多说,大家都用到过.讲象我一开始说到的那样,天气预报员带着他的天气预报一样
那都是上帝预先安排好的那样,刮风下雨,下雪,冰雹等等.直接拿来使用就好了.就想一个按纽那样
点击.鼠标进入,鼠标移开,等等.例子我就不给了,谁都用过,
好了,我写完了,希望对你有点帮助,当然看上去,更象一堆废话,是吗?那好吧,你就当成是一种
消遣.最后还是谢谢各位能够看完.我还会写的.就这样,下回见.