C#中事件的定义和使用
事件的声明和使用与代理有很密切的关系,事件其实是一个或多个方法的代理,当对象的某个状态发生了变化,代理会被自动调用,从而代理的方法就被自动执行。
声明和使用一个事件需要如下步骤:
1.创建一个代理。
2.在类的内部利用event关键字声明事件,并且在类中定义调用事件的方法,也可以定义一个处理事件消息的方法。
声明一个事件的基本形式有两种:
修饰符 event 类型 标识符
修饰符 event 类型 标识符{get{};set{};}
其中:
修饰符是指c#语言的访问修饰符;类型是在声明使用事件的第一步中创建的代理;标识符是一个c#语言的合法标识符,这个标识符被用来在程序中唯一确定声明的事件;在声明事件的第二种形式中具有和类的属性类似的get和set部分,事件的get和set部分意义、使用方法均与属性类似。
3.声明若干个方法与事件关联。将事件同方法进行关联的过程类似于代理对象引用方法的过程。这些方法就是用来处理事件的方法。
4.最后,在程序中使用事件。使用事件需要根据事件的定义或者直接调用事件已经定义好的调用事件的方法,或者通过系统消息来自动触发事件。
用一个例子来说明事件的使用。
创建一个简单的类,名为filewatch,包含事件onfilechange。该类将检查在执行应用程序的目录(当前目录,通常是项目名/bin/debug)下,是否存在文件test.txt。如果文件被删除或创建,都将触发事件。同时提供一个方法monitorfile以不断地查询该文件。
方法:
在创建一个可用事件之前,首先声明一个委托,放在类的外面。
接下来创建类filewatch。然后声明事件,注意事件的类型即为我们之前定义的委托。
现在创建方法onfilechange(),当调用该方法时将触发事件:
{
if (filewatchevent!=null)
{
filewatchevent(this,e);
}
}
最后,创建方法monitorfile(),
{
bool bcurrentstatus;
while(true)
{
bcurrentstatus = file.exists("test.txt");
if (bcurrentstatus != _blaststatus) //_blaststatus为私有字段,初始值为false;
{
_blaststatus = bcurrentstatus;
onfilechange(eventargs.empty);
}
thread.sleep(250);
}
}
完整代码如下:
using system.threading;
using system.io;
namespace sample.event
{
public delegate void filewatcheventhandler(object sender, eventargs e);
public class filewatch
{
private bool _blaststatus = false;
public filewatch()
{
//
// todo: 在此处添加构造函数逻辑
//
}
public event filewatcheventhandler filewatchevent;
protected virtual void onfilechange(eventargs e)
{
if (filewatchevent != null)
{
filewatchevent(this, e);
}
}
public void monitorfile()
{
bool bcurrentstatus;
while(true)
{
bcurrentstatus = file.exists("test.txt");
//状态不符,说明文件被删除或重新创建,此时触发事件;
if ( bcurrentstatus != _blaststatus )
{
_blaststatus = bcurrentstatus;
onfilechange( eventargs.empty );
}
thread.sleep(250);
}
}
}
}
使用:创建一个windows应用程序来测试我们之前建立的filewatch中的事件。首先将刚才创建的项目编译,生成assembly:filewatch.dll,然后添加引用。当然直接添加项目引用也可以。然后在windows应用程序中加入命名空间:
然后在应用程序类中,定义一个私有字段,类型为我们之前创建的类filewatch:
{
private sample.event.filewatch filewatcheventsource;
并在构造函数中实例化该对象;
{
initializecomponent();
filewatcheventsource = new sample.event.filewatch();
然后将本地方法onfilechange连接到事件中:
我们需要调用monitorfile方法来触发事件。在本例中,我们用线程来控制monitorfile方法。这样可以在
应用程序闲置的时候运行该方法以触发事件。
thrd.start();
最后,我们需要知道事件是否被触发,为了记录下事件触发的历史记录,我们在listbox控件添加触发内容。由于事件触发后调用的方法是onfilechange,因此我们将操作放在该方法里:
{
listbox.items.add(datetime.now.tostring()+": 文件发生改变.");
}
当触发事件时,eventhanler会传递sender和eventargs类的引用。eventargs类通常是在事件源和触发事件器之间传递信息。在本例中,没有传递信息,也没有用到eventargs类。而只是将事件添加到了listbox中。
运行结果如下:
结论:在c#中使用事件的要点:
首先,要建立委托,格式为:
public delegate void 委托名(object sender, eventargs e);
注意:委托即c里的函数指针,在事件中由于要传递事件及触发事件的对象的信息,因此参数表是固定的。委托名一般格式是:名字+envenhandle。这样取名比较规范。
然后,建立一个事件字段:
public event 委托类型 事件名;
注意:event关键字代表事件,返回类型为委托;
再定义一个方法,处理事件,再本例中为onfilechange(eventargs e)。在该方法中应该调用事件:
事件名(object ,eventargs);
这里object一般为本身,实参应为this,eventargs应为onfilechange中传递过来的实参,尤其是要传递事件的值。
最后还要创建触发事件的方法。例子中为monitorfile(),在其方法中,当条件满足则调用onfilechange来达到触发事件的目的。
在使用事件时,通常要定义两个方法,一个是和事件定义的委托签名一致的方法,在本例中为onfilechange(object sender,eventargs e);
注意在例子中,窗体类的onfilechange和事件类的onfilechange是不同的。后者用于调用事件,前者则用于绑定事件。绑定事件的方法很简单,用+=表示添加事件,-=表示删除事件。
例子中
filewatcheventsource.filewatchevent+=new sample.event.filewatcheventhandler(onfilechange);
即为添加事件。
例子中:首先线程启动(thd.start()),然后调用monitorfile()方法。引起事件产生,filewatchevent产生后,由于我们将事件filewatchevent绑定到了onfilechange()方法上。因而调用本地即窗口类的onfilechange()方法,从而实现在listbox中添加信息。