答案: 下面的文章是我自认为对"事件"机制理解比较透彻的后写的代码分析,但写完之后,鄙人又惟恐理解有所偏差,所以特贴出来让各位高手指教一二,若能让发现理解错误之处,将不胜感激(此问完全乃本人"独立自主"之作,绝非抄袭)
同时我相信此文对c#初学者也有一定帮助!
为了阐述清晰,特举例说明;
该范例是在一个控件中完全自定义一组事件,并在另外的程序集中对事件被激发作出反映(也就是事件被激发后调用预先定义好的方法).
一.含有自定义事件的控件主体代码及对应剖析 (注意,此控件库是由VS.NET的"新建"->"Windows控件库"生成的)
namespace MyEventTEST
{
public class LoginEventArgs : System.EventArgs
// 上面代码定义了在主程序中引发事件时需要传递给主程序的所有信息,并且注意,
// 该类必须派生于System.EventArgs类
{
public LoginEventArgs(string sUserID, string sPassword, bool bValid)
{
UserID = sUserID;
Password = sPassword;
Valid = bValid;
}
public string UserID;
public string Password;
public bool Valid;
}
public delegate void GoodLoginEventHandler(object sender, LoginEventArgs e);
public delegate void FailedThreeTimesEventHandler(object sender, LoginEventArgs e);
// 上面两行代码定义了两个多路委托(因此返回类型必须为void),每个委托对应一种类型的事件;
// 因为是多路委托,所以每个委托中可以含有多个方法.
// 请注意,参数是(object sender, LoginEventArgs e),所以添加到多路委托的方法必须符合这种签名方式.
// 此外,为什么这里不使用系统已经定义的多路委托"System.EventHandler(object sender, EventArgs e)",
// 而要自己定义新的委托"?????EventHandler()"呢?这是因为我们这里传递给用户程序集的参数不是
// "System.EventArgs"类型,而是自己定义的"LoginEventArgs"类型,所以有必要重新定义自己的委托类型.
public class ActiveLogin : System.Windows.Forms.UserControl
{
private System.Windows.Forms.Label label1;
private System.Windows.Forms.Label label2;
private System.Windows.Forms.TextBox txtUserID;
private System.Windows.Forms.TextBox txtPass;
private System.Windows.Forms.Button btnLogin;
private System.Windows.Forms.Button btnCancel;
private System.ComponentModel.Container components = null;
// 上面代码是组成这个控件的一些组件定义,由VS.NET自动生成
public event GoodLoginEventHandler GoodLogin;
public event FailedThreeTimesEventHandler FailedThreeTimes;
public event EventHandler Cancel;
// 上面三行代码非常之重要,定义了三个事件(event),分别是"GoodLogin","FailedThreeTimes"
// 和"Cancel"
// 它们的类型分别是"GoodLoginEventHandler","FailedThreeTimesEventHandler"
// 和"EventHandler",也就是说添加到这三个事件中的方法必须符合对应的多路委托定义!
// 而且注意,因为事件"Cancel"的类型是系统已经定义的多路委托"EventHandler"类型,
// 所以上面的多路委托中没有定义类似"CancelEventHandler"的新委托,因为是不需要的.
public ActiveLogin()
{
InitializeComponent();
}
// 上面代码是控件中类"ActiveLogin"的构造方法,该方法中调用了初始方法InitializeComponent()
// 上面代码由VS.NET自动生成
protected override void Dispose( bool disposing )
{
if( disposing )
{
if(components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}
// 上面代码是自定义控件中类"ActiveLogin"的析构方法,由VS.NET自动生成.
private void InitializeComponent()
{
.... // 这里是对所有引用控件(组件)的初始化代码
}
// 上面代码是自定义控件中类"ActiveLogin"的初始方法,其中内容由VS.NET自动生成.
protected virtual void OnGoodLogin(LoginEventArgs e)
// 上面一行代码定义了激发"GoodLogin"事件的方法;
// 注意签名类型,定义方法是protected virtual,也就是说只能在这个类及它的
// 继承类中访问此方法,而且可以重写.
// 参数类型是"LoginEventArgs",注意只有这一个参数,因为在本方法中含有对
// this的引用,所以这里不需要传递this对象.
// 一般地说,这个方法使用场合只有两种:
// <1>在本控件内被调用,因为本方法不被调用,就无法激发用户代码在事件"GoodLogin"
// 中添加的方法;
// <2>在用户的继承代码中重写本方法,虽然重写本方法可能会带来性能的提高,但
// 倘若用户代码中忘记调用此方法,那么在用户代码先前在事件"GoodLogin"中
// 添加的方法将无法得到激活!!! (避免此问题的方法就是在重写方法中必须含有
// 一行"base.GoogLogin(e)",这行将负责调用本方法)
// 对于第<2>点需要提出的是,在用户的继承代码中重写本方法的作用相当与在
// 事件"GoodLogin"中添加一个方法,此方法的代码内容和重写方法内容相同.
// (但是应该绝对没有"base.GoogLogin(e)"这一行)
{
if (GoodLogin != null) // 如果在事件"GoogLogin"中含有方法,则激发这些方法
{
GoodLogin(this, e); // 把this对象和参数e传递给所有在事件"GoogLogin"
// 中添加的方法,并顺序执行这些方法 (注意,由多路
// 委托特性决定:在用户代码中先添加的方法先执行.
}
}
// 上面对OnGoogLogin方法解释已经十分详细了,下面两个ON方法均与上述ON方法同出一辙.
protected virtual void OnFailedThreeTimes(LoginEventArgs e)
{
if (FailedThreeTimes != null)
{
FailedThreeTimes(this, e);
}
}
protected virtual void OnCancel(System.EventArgs e)
{
if (Cancel != null)
{
Cancel(this, e);
}
}
private void btnLogin_Click(object sender, System.EventArgs e)
// 上面的定义是由VS.NET自动生成,是当按下控件的"btnLogin"按钮时调用的方法.
{
if(...)
OnGoodLogin(new LoginEventArgs(txtUserID.Text, txtPass.Text, true));
// 上面一行代码调用了OnGoodLogin方法,作用是"当控件中的按钮btnLogin被按下时,
// 并且符合上面的if条件时:
// 将通过调用OnGoodLogin方法把在用户代码中添加到事件"GoogLogin"中
上一个:.NET中的Exception处理(C#)
下一个:ASP.NET Web 服务还是 .NET Remoting:如何选择(2)