##1.委托与事件的关系
事件是一类特殊的委托,更像是委托的升级版,将委托中的方法封装起来,封装到触发事件的函数或类中,当调用某一函数时,函数触发该事件,执行事件中的委托方法。
##2.事件定义格式
修饰词 envent 委托名称 事件名称
例如:public event SaySomething come
事件传入参数类型与返回值类型跟绑定委托一摸一样,即事件只会跟绑定的委托打交道,当触发该事件时,事件会通知关注的委托。
##3.事件的使用实例
定义委托SaySomething和2个委托方法SayHello、SayNiceToMeetYou,定义事件OnCome,在方法test中,添加实例化委托方法sayhello、saynice到事件OnCome 中,再触发该事件OnCome,主函数Main中调用program.test();即触发事件
internal class Program
{
public delegate void SaySomething(string name); //定义一个委托
public void SayHello(string name) //委托实例方法
{
Console.WriteLine("Hello," + name + "!");
}
public void SayNiceToMeetYou(string name) //委托实例方法
{
Console.WriteLine("Nice to meet you," + name + "!");
}
public event SaySomething OnCome; //定义事件OnCome,事件传入的方法与返回值类型同委托SaySomething一致
public void test()
{
SaySomething sayhello = new SaySomething(SayHello); //实例化委托并传入方法
SaySomething saynice = new SaySomething(SayNiceToMeetYou); //实例化委托并传入方法
OnCome += sayhello; //将方法添加到事件OnCome中
OnCome += saynice; //将方法添加到事件OnCome中,多播委托
OnCome("张三"); //事件执行实例
}
static void Main(string[] args)
{
Program program = new Program();
program.test(); //事件好处:如果代码升级,并不需要修改调用的地方,
//即program.test();因为事件相当于已经被封装起来了。
//只需要修改委托实例,C#语言代码核心:高内聚,低耦合
Console.Read();
}
}
##3.事件的自定义模式
常见的窗体按钮单击事件、文本框聚焦事件、列表框选择事件等,此部分事件及背后的委托方法,已经被.net封装起来可直接使用,但我们可自定义事件的发生,事件例子代码如下:事件中的详细信息已经再代码中做了批注
public delegate void TimeEventHandler(object obj, TimeEventArgs args); //定义一个委托,
//委托其实就是“方法模板”,就好像“类”是“对象”的模板一样。
//如果某个类想在事件触发的时候收到通知,它必须有一个符合这种格式的方法,
//在这个例子中,就是:返回类型为void,参数类型为object、TimeEventArgs
//TimeEventArgs是我们自己定义的一个类,用于保存事件中的参数。这里我们分别保存时间的时分秒。
public class TimeEventArgs : EventArgs //继承至EventArgs类
{
private int hour; //类中定义3个字段
private int minute;
private int second;
public int Hour
{
get
{
return hour;
}
} //字段属性
public int Minute
{
get
{
return minute;
}
}
public int Second
{
get
{
return second;
}
}
public TimeEventArgs(int hour, int minute, int second) //以类名定义的方法,构造函数,相当于重写或者重载形式
{
this.hour = hour;
this.minute = minute;
this.second = second;
}
}
//这是一个观察者类,它有一个符合我们上面定义的“委托”的方法,
//也就是void ShowTime(object obj, TimeEventArgs args),
//从这个方法的定义可以看到,我们只会关心返回类型和方法的参数,而方法名称则无所谓
class MyTimeEventHandlerClass
{
public void ShowTime(object obj, TimeEventArgs args) //TimeEventHandler委托的实例
{
Console.WriteLine("现在的时间是:" + args.Hour + ":" + args.Minute + ":" + args.Second);
}
}
//时钟类
class Clock
{
我们在这个类中定义了一个“TimeChanged”事件,
注意其前面有两个关键字“event”和“TimeEventHandler”,
其中event表示这是一个事件,而不是方法或属性;TimeEventHandler则指出,
谁要监听TimeChanged事件,它就必须有一个符合TimeEventHandler(委托)的方法。
public event TimeEventHandler TimeChanged;
public Clock()
{
TimeChanged = null; //注意,这里的null的含义是指TimeChanged事件当前还没有观察者关注它,
//如果某个观察者要关注TimeChanged事件,它必须要让这个事件知道,
//方法是使用操作符“+=”来借助委托将其加载到事件上。
}
//时钟开始走动,我们的目标是每秒钟触发一次TimeChanged事件
public void go()
{
DateTime initi = DateTime.Now;
int h1 = initi.Hour;
int m1 = initi.Minute;
int s1 = initi.Second;
while (true)
{
DateTime now = DateTime.Now;
int h2 = now.Hour;
int m2 = now.Minute;
int s2 = now.Second;
if (s2 != s1)
{
h1 = h2;
m1 = m2;
s1 = s2;
TimeEventArgs args = new TimeEventArgs(h2, m2, s2); //实例化类TimeEventArgs,并传入3个参数
//首先建立一个TimeEventArgs对象来保存相关参数,这里是时分秒。
TimeChanged(this, args); //事件执行
}
}
}
}
internal class Program
{
static void Main(string[] args)
{
Clock clock = new Clock(); //实例化一个时钟
MyTimeEventHandlerClass tehc = new MyTimeEventHandlerClass(); //实例化一个观察者类
//委托事件TimeChanged与委托方法tehc.ShowTime绑定,即事件TimeChanged触发时,ShowTime方法响应
clock.TimeChanged += new TimeEventHandler(tehc.ShowTime);
clock.go(); //clock.go方法中会触发TimeChanged事件
Console.ReadLine();
}
}
##4事件的组成部分
综上所述一个完整的事件过程应该由以下部分组成:
##5.根据上面的例子总结事件的使用
事件使用:
delegate是C#中的一种类型,它实际上是一个能够持有对某个方法的引用的类。与其它的类不同,delegate类能够拥有一个签名(signature),并且它"只能持有与它的签名相匹配的方法的引用"。它所实现的功能与C/C++中的函数指针十分相似。它允许你传递一个类A的方法m给另一个类B的对象,使得类B的对象能够调用这个方法m。但与函数指针相比,delegate有许多函数委托和事件在 .Net Framework中的应用非常广泛指针不具备的优点。首先,函数指针只能指向静态函数,而delegate既可以引用静态函数,又可以引用非静态成员函数。在引用非静态成员函数时,delegate不但保存了对此函数入口指针的引用,而且还保存了调用此函数的类实例的引用。其次,与函数指针相比,delegate是面向对象、类型安全、可靠的受控(managed)对象。也就是说,runtime能够保证delegate指向一个有效的方法,你无须担心delegate会指向无效地址或者越界地址。
实现一个delegate是很简单的,通过以下3个步骤即可实现一个delegate:
1. 声明一个delegate对象,它应当与你想要传递的方法具有相同的参数和返回值类型。
2. 创建delegate对象,并"将你想要传递的函数作为参数传入"。
3. 在要实现异步调用的地方,通过上一步创建的对象来调用方法。
C#中的事件处理实际上是一种具有特殊签名的delegate,象下面这个样子:
public delegate void MyEventHandler(object sender, MyEventArgs e);
其中的两个参数,sender代表事件发送者,e是事件参数类。MyEventArgs类用来包含与事件相关的数据,所有的事件参数类都必须从System.EventArgs类派生。当然,如果你的事件不含参数,那么可以直接用System.EventArgs类作为参数。
就是这么简单,结合delegate的实现,我们可以将自定义事件的实现归结为以下几步:
1.定义delegate对象类型,它有两个参数,第一个参数是事件发送者对象,第二个参数是事件参数类对象。
2.定义事件参数类,此类应当从System.EventArgs类派生。如果事件不带参数,这一步可以省略。
3.定义"事件处理方法,它应当与delegate对象具有相同的参数和返回值类型"。
4.用event关键字定义事件对象,它同时也是一个delegate对象。
5.用+=操作符添加事件到事件队列中(-=操作符能够将事件从队列中删除)。
6.在需要触发事件的地方用调用delegate的方式写事件触发方法。一般来说,此方法应为protected访问限制,既不能以public方式调用,但可以被子类继承。名字是OnEventName。
7. 在适当的地方调用事件触发方法触发事件。