其实在MSDN上已经有大量关于事件的文章,而且写得非常规范,但是比较难以理解
要说事件,先说委托,要说委托,先说指针,另外,再加上对Windows消息循环的说明。
我们知道Windows是基于消息的操作系统,一个典型的Windows应用程序启动后就开始执行一个等待消息的无限循环,操作系统会将消息分发给线程,线程又将消息用switch开关分发给不同的函数得以执行。Windows消息模型和.net事件模型行为非常相似,不过他们不处于一个层次上,Windows消息比较原始。
再来说委托,在.net中,函数体比较标准的叫法叫做“方法”,我们可以为方法指定形参,行参可以是一些简单的数据类型,如int等,甚至也可以是对象等引用类型。但我们却没有办法将另一个方法名当作形参传递给这个方法。委托的作用就是封装一个方法,使其看起来像是一个引用类型,从而可以当作方法的形参而传递。如果放在c++里,委托更像一个指向方法的指针,C#中不提倡指针(并非不允许,如果你愿意可以在unchecked语句块中使用指针),代替指针行使功能的就是委托。
事件是一种特殊的委托,它特殊在什么地方呢?特殊再它是一个多路广播委托,这个怎么理解?通常我们所声明的代理是一个单播代理,也就是一次只能有一个方法委托给它,但事件不同,可以将多个方法委托给它,当该代理被调用的时候,它将同时通知所有委托到它上面的方法开始执行。好比,声明事件的类是事件的发布者,而其他类里注册该事件方法的称之为订阅者。如果订阅书刊一样,订阅者实现约定好要订阅的书刊(事件),当书刊被印刷出版(引发了事件)以后,书刊被送达订阅者手中(执行方法)。
从声明事件的语句上便可看出:
先声明一个代理:
delegate void EventHandler();
再在该代理的基础上声明事件:
event EventHandler Event
正因为事件是一个多路广播委托,因此注册事件处理程序和注销事件处理程序就是用+=和-=二元运算符来操作,而不是使用等号。这一点应该不难理解吧? 事件维护一个列表用来存放代理,当事件被引发时,通知列表中的所有代理。而等号却只能赋予其一个代理,且下次等号赋值将覆盖上一次赋予的代理,形成不了列表,也就不能产生多路广播委托的效果了。实际上,+=和-=这两个二元运算符在Event中进行了重载。
要使用事件,必须先注册他(也就是订阅)
注册一个事件:
Event += new EventHandler(EventMethod)
也可以注册多个事件:
Event += new EventHandler(EventMethod1);
Event += new EventHandler(EventMethod2);
...
在VS中,注册事件这个过程被隐藏了。以一个按钮控件来说,只需要双击该按钮,就默认注册了按钮的Click事件,并且VS自动将焦点定位到了当Click事件引发时执行的方法内了。如果你打开窗体名称.Designer.cs文件,展开InitializeComponent方法,就可以看到如上注册事件的语句了。
Button1.Click += EventHandler(Button1_Click);
订阅了事件,并不意味着订阅该事件的方法就会执行,如上述,只有在用户单击了按钮的时候,Click事件发生,Button1_Click方法才得以执行。
那么上面所说的都是针对订阅者的,我们清楚认识到要引发事件必须单击按钮。不过,如果你在开发一个控件、组件、类的时候,在你的控件、组件或类里声明了事件,那么事件到底在何时,由谁来引发呢?答案是:你自己!
假设,我们设计一个控件,声明了一个事件:
delegate void EventHandler(object sender, EventArgs e);
event EventHandler PreProcess;
声明一个方法:
void StartProcess();
我们希望使用该控件的用户在调用StartProcess方法,方法执行前引发Event事件,以便于用户有机会在真正处理开始前有机会做一些事情(比如在窗体上显示“正在处理”字样),那么我们就需要在StartProcess方法开始时,引发该事件:
void StartProcess()
{
if (PreProcess != null)
PreProcess(this, new EventArgs());
...//剩下的处理代码。
}
而在使用该控件的窗体中,
设一个方法:
void Control1_PreProcess(object sender, EventArgs e)
{
...//当处理开始时要执行的代码
}
以该方法注册到PreProcess事件(这段代码可以放在这个事件引发之前就执行的其他方法体中):
Control1.PreProcess += new EventHandler(Control1_PreProcess);
后记:
通常大多数朋友使用.net已有的事件,实际上大多数时候我们是事件的订阅者,而要开发控件、组件、类时,我们就需要学习如何发布事件,以及如何在适当的时机引发事件。
当然,我上面所述尽量以一种通俗的说法来阐明事件的原理,以及如何编写它、使用它。限于水平,难免出错欢迎指正。
另外,如果要想编写优秀的、高质量的事件,那么MSDN上关于事件编写的原则不得不熟悉一下,下面简要摘抄几条:
1、通常,我们应该以使用.net已定义的EvengHandler代理和事件所需传递参数EventArgs类来声明事件,如果这个代理和EventArgs类不能满足要求,应当从它们开始继承你自己的代理和EventArgs类。
2、通常,事件应当传递两个参数,即object sender,表示引发事件的发布者(控件、组件),和一个EventArgs e,表示事件所需传递的参数,不建议自行构造参数列表。
3、事件没有返回值,因此代理的返回值应为void,且注册事件的方法也是void。事件的发布者通常只是负责引发事件,它们不关心事件的返回,也关心不了。
4、如果可能,请使用EventHandler的泛型版本:EventHandler<T>,以保持设计一致。
要说事件,先说委托,要说委托,先说指针,另外,再加上对Windows消息循环的说明。
我们知道Windows是基于消息的操作系统,一个典型的Windows应用程序启动后就开始执行一个等待消息的无限循环,操作系统会将消息分发给线程,线程又将消息用switch开关分发给不同的函数得以执行。Windows消息模型和.net事件模型行为非常相似,不过他们不处于一个层次上,Windows消息比较原始。
再来说委托,在.net中,函数体比较标准的叫法叫做“方法”,我们可以为方法指定形参,行参可以是一些简单的数据类型,如int等,甚至也可以是对象等引用类型。但我们却没有办法将另一个方法名当作形参传递给这个方法。委托的作用就是封装一个方法,使其看起来像是一个引用类型,从而可以当作方法的形参而传递。如果放在c++里,委托更像一个指向方法的指针,C#中不提倡指针(并非不允许,如果你愿意可以在unchecked语句块中使用指针),代替指针行使功能的就是委托。
事件是一种特殊的委托,它特殊在什么地方呢?特殊再它是一个多路广播委托,这个怎么理解?通常我们所声明的代理是一个单播代理,也就是一次只能有一个方法委托给它,但事件不同,可以将多个方法委托给它,当该代理被调用的时候,它将同时通知所有委托到它上面的方法开始执行。好比,声明事件的类是事件的发布者,而其他类里注册该事件方法的称之为订阅者。如果订阅书刊一样,订阅者实现约定好要订阅的书刊(事件),当书刊被印刷出版(引发了事件)以后,书刊被送达订阅者手中(执行方法)。
从声明事件的语句上便可看出:
先声明一个代理:
delegate void EventHandler();
再在该代理的基础上声明事件:
event EventHandler Event
正因为事件是一个多路广播委托,因此注册事件处理程序和注销事件处理程序就是用+=和-=二元运算符来操作,而不是使用等号。这一点应该不难理解吧? 事件维护一个列表用来存放代理,当事件被引发时,通知列表中的所有代理。而等号却只能赋予其一个代理,且下次等号赋值将覆盖上一次赋予的代理,形成不了列表,也就不能产生多路广播委托的效果了。实际上,+=和-=这两个二元运算符在Event中进行了重载。
要使用事件,必须先注册他(也就是订阅)
注册一个事件:
Event += new EventHandler(EventMethod)
也可以注册多个事件:
Event += new EventHandler(EventMethod1);
Event += new EventHandler(EventMethod2);
...
在VS中,注册事件这个过程被隐藏了。以一个按钮控件来说,只需要双击该按钮,就默认注册了按钮的Click事件,并且VS自动将焦点定位到了当Click事件引发时执行的方法内了。如果你打开窗体名称.Designer.cs文件,展开InitializeComponent方法,就可以看到如上注册事件的语句了。
Button1.Click += EventHandler(Button1_Click);
订阅了事件,并不意味着订阅该事件的方法就会执行,如上述,只有在用户单击了按钮的时候,Click事件发生,Button1_Click方法才得以执行。
那么上面所说的都是针对订阅者的,我们清楚认识到要引发事件必须单击按钮。不过,如果你在开发一个控件、组件、类的时候,在你的控件、组件或类里声明了事件,那么事件到底在何时,由谁来引发呢?答案是:你自己!
假设,我们设计一个控件,声明了一个事件:
delegate void EventHandler(object sender, EventArgs e);
event EventHandler PreProcess;
声明一个方法:
void StartProcess();
我们希望使用该控件的用户在调用StartProcess方法,方法执行前引发Event事件,以便于用户有机会在真正处理开始前有机会做一些事情(比如在窗体上显示“正在处理”字样),那么我们就需要在StartProcess方法开始时,引发该事件:
void StartProcess()
{
if (PreProcess != null)
PreProcess(this, new EventArgs());
...//剩下的处理代码。
}
而在使用该控件的窗体中,
设一个方法:
void Control1_PreProcess(object sender, EventArgs e)
{
...//当处理开始时要执行的代码
}
以该方法注册到PreProcess事件(这段代码可以放在这个事件引发之前就执行的其他方法体中):
Control1.PreProcess += new EventHandler(Control1_PreProcess);
后记:
通常大多数朋友使用.net已有的事件,实际上大多数时候我们是事件的订阅者,而要开发控件、组件、类时,我们就需要学习如何发布事件,以及如何在适当的时机引发事件。
当然,我上面所述尽量以一种通俗的说法来阐明事件的原理,以及如何编写它、使用它。限于水平,难免出错欢迎指正。
另外,如果要想编写优秀的、高质量的事件,那么MSDN上关于事件编写的原则不得不熟悉一下,下面简要摘抄几条:
1、通常,我们应该以使用.net已定义的EvengHandler代理和事件所需传递参数EventArgs类来声明事件,如果这个代理和EventArgs类不能满足要求,应当从它们开始继承你自己的代理和EventArgs类。
2、通常,事件应当传递两个参数,即object sender,表示引发事件的发布者(控件、组件),和一个EventArgs e,表示事件所需传递的参数,不建议自行构造参数列表。
3、事件没有返回值,因此代理的返回值应为void,且注册事件的方法也是void。事件的发布者通常只是负责引发事件,它们不关心事件的返回,也关心不了。
4、如果可能,请使用EventHandler的泛型版本:EventHandler<T>,以保持设计一致。
铲铲是也