形象的说,事件(event)就是类或对象用来“发出通知”的成员。通过提供事件的句柄,客户能够把事件和可执行代码联系在一起。
让我们一起先来看一个事件的例子。如果你熟悉MFC的话,理解这个例子应该不会很难。
程序清单13-1:
public delegate void EventHandler(object sender,EventArgs e);
public class Button:Control
{
public event EventHandler Click;
protected void OnClick(EventArgs e){
if(Click!=null) Click(this,e);
}
public void Reset(){
Click=null;
}
}在这个例子中,Click是类Button的一个域,我们可以获得它的值并进行修改。OnClick方法用于触发Click事件。
C#事件的声明
事件的声明格式:
attributes event-modifiers event type variable-declarators;
attributes event-modifiers event type member-name
{event-accessor-declarations}
事件的修饰符event-modifier可以是:
●new
●public
●protected
●internal
●private
●static
●virtual
●sealed
●override
●abstract
static,virtual,override和abstract修饰符同一时刻只能出现一个。事件的声明中可以包含事件访问说明,或者依靠编译器自动提供一个访问器;它也可以省略事件访问说明,一次定义一个或多个事件。上面的例子中就省略了这个说明。
注意:使用了abstract修饰符的抽象事件中必须省略事件访问说明,否则编译器会提示错误。
事件所声明的类型必须是一个代表(delegate)类型,代表类型应预先声明,如上例中的public delegate void EventHandler。
事件的预订和撤消
在随后的例子中,我们声明了一个使用Button类的登录对话框类。对话框类含有两个按钮:OK和Cancel按钮。
程序清单13-2:
public class LoginDialog Form
{
Button OkButton;
Button CancelButton;
public LoginDialog(){
OkButton=new Button(...);
OkButton.Click+=new EventHandler(OkButtonClick);
CancelButton=new Button(...);
CnacelButton.Click+=new EventHandler(CancelButtonClick);
}
void OkButtonClick(object sender,EventArgs e){
//处理OkButton.Click事件
}
void CancelButtonClick(object sender,EventArgs e){
//处理CancelButton.Click事件
}
}在例子中使用了Button类的两个实例,事件的预订是通过为事件加上左操作符“+=”来实现的:
OkButton.Click+=new EventHandler(OkButtonClick);
这样,只要事件被触发,方法就会被调用。
事件的撤消则采用左操作符“-=”:
OkButton.Click-=new EventHandler(OkButtonClick);
如果在类中声明了事件,我们又希望象使用域的方式那样使用事件,那么这个事件就不能是抽象的,也不能显式地包含事件访问声明。满足了这两个条件后,在任何可以使用域的场合都同样可以使用事件。
注意:对事件的触发相当于调用事件所表示的原型-delegate,所以对delegate型原型的调用必须先经过检查,确保delegate不是null型的。
事件访问器
如Button的例子所示,大多数情况下事件的声明都省略了事件访问声明。什么情况下使用事件访问声明呢?答案是:如果每个事件的存储开销太大,我们就可以在类中包含事件访问声明,按私有成员的规则存入事件句柄列表。
访问器的声明包括两种:添加访问器声明(add-accessor-declaration)和删除访问器声明(remove-accessor-declaration)。
访问器声明之后跟随相关执行代码的语句块。在添加访问器声明后的代码需要执行添加事件句柄的操作,在删除访问器声明后的代码需要执行删除事件句柄的操作。不管是哪种事件访问器,都对应相应的一个方法,这个方法只有一个事件类型的值参数,并且返回值为void。
在执行预订操作时使用添加型访问器,在执行撤消操作时使用删除型访问器。访问器中实际上还包含了一个名为value的隐藏的参数,因而访问器在使用局部变量时不能再使用这个名字。
下面给出了使用访问器的例子。
程序清单13-3:
class Control:Component
{
//Unique keys for events
static readonly object mouseDownEventKey=new object();
static readonly object mouseUpEventKey=new object();
//Return event handler associated with key
protected Delegate GetEventHandler(object key){...}
//Add event handler associated with key
protected void AddEventHandler(object key,Delegate handler){...}
//Remove event handler associated with key
protected void RemoveEventHandler(object key,Delegate handler){...}
//MouseDown event
public event MouseEventHandler MouseDown{
add{AddEventHandler(mouseDownEventKey,value);}
remove{AddEventHandler(mouseDownEventKey,value);}
}
//MouseUp event
public event MouseEventHandler MouseUp{
add{AddEventHandler(mouseUpEventKey,value);}
remove{AddEventHandler(mouseUpEventKey,value);}
}
}
静态事件
静态事件不与具体的实例相关联,因此不能在静态事件的访问器中引用this关键字。此外,在静态事件声明时又加上virtual,abstract或override修饰符也都是不合法的。而对于非静态的事件,我们可以在事件的访问器中使用this来指代类的实例。
C#索引指示器的声明
索引指示器的声明格式:
attributes indexer-modifiers indexer-declarator
{accessor-declarations}
索引指示器可以使用的修饰符indexer-modifier有:
●new
●public
●protected
●internal
●private
●virtual
●sealed
●override
●abstract
一对大括号“{}”之间是索引指示器的访问声明,使用get关键字和set关键字定义了对被索引的元素的读写权限。
例如,下面的例子用于打印出小组人员的名单。
程序清单13-4:
using System;
class Team
{
string s_name=new string[8];
public string this[int nIndex]
{
get{
return s_name[nIndex];
}
set{
s_name[nIndex]=value;
}
}
}
class Test
{
public static void Main(){
Team t1=new Team();
for(int i=0;i<6;i++)
Console.WriteLine(t1[i]);
}
}在许多情况下,某些数据信息应该是属于类或类的实例所私有的,需要限制对这些信息的访问。而我们有时又不希望这类数据对外界完全封闭。和属性一样,索引指示器为我们提供了控制访问权限的另一种方法。
请发表评论