深入解析事件 事件的由来在介绍事件之前大家可以先看看下面的例子 PriceManager 负责对商品价格进行处理当委托对象 GetPriceHandler 的返回值大于100元按8.8折计算低于100元按原价计算。1 public delegate double PriceHandler(); 2 3 public class PriceManager 4 { 5 public PriceHandler GetPriceHandler; 6 7 //委托处理当价格高于100元按8.8折计算其他按原价计算 8 public double GetPrice() 9 { 10 if (GetPriceHandler.GetInvocationList().Count() 0) 11 { 12 if (GetPriceHandler() 100) 13 return GetPriceHandler()*0.88; 14 else 15 return GetPriceHandler(); 16 } 17 return -1; 18 } 19 } 20 21 class Program 22 { 23 static void Main(string[] args) 24 { 25 PriceManager priceManager new PriceManager(); 26 27 //调用priceManager的GetPrice方法获取价格 28 //直接调用委托的Invoke获取价格两者进行比较 29 priceManager.GetPriceHandler new PriceHandler(ComputerPrice); 30 Console.WriteLine(string.Format(GetPrice\n Computers price is {0}!, 31 priceManager.GetPrice())); 32 Console.WriteLine(string.Format(Invoke\n Computers price is {0}!, 33 priceManager.GetPriceHandler.Invoke())); 34 35 Console.WriteLine(); 36 37 priceManager.GetPriceHandler new PriceHandler(BookPrice); 38 Console.WriteLine(string.Format(GetPrice\n Books price is {0}!, 39 priceManager.GetPrice())); 40 Console.WriteLine(string.Format(Invoke\n Books price is {0}! , 41 priceManager.GetPriceHandler.Invoke())); 42 43 Console.ReadKey(); 44 } 45 //书本价格为98元 46 public static double BookPrice() 47 { 48 return 98.0; 49 } 50 //计算机价格为8800元 51 public static double ComputerPrice() 52 { 53 return 8800.0; 54 } 55 }运行结果观察运行的结果如果把委托对象 GetPriceHandler 设置为 public 外界可以直接调用 GetPriceHandler.Invoke 获取运行结果而移除了 GetPrice 方法的处理这正是开发人员最不想看到的。为了保证系统的封装性开发往往需要把委托对象 GetPriceHandler 设置为 private, 再分别加入 AddHandlerRemoveHandler 方法对 GetPriceHandler 委托对象进行封装。1 public delegate double PriceHandler(); 2 3 public class PriceManager 4 { 5 private PriceHandler GetPriceHandler; 6 7 //委托处理当价格高于100元按8.8折计算其他按原价计算 8 public double GetPrice() 9 { 10 if (GetPriceHandler!null) 11 { 12 if (GetPriceHandler() 100) 13 return GetPriceHandler()*0.88; 14 else 15 return GetPriceHandler(); 16 } 17 return -1; 18 } 19 20 public void AddHandler(PriceHandler handler) 21 { 22 GetPriceHandler handler; 23 } 24 25 public void RemoveHandler(PriceHandler handler) 26 { 27 GetPriceHandler - handler; 28 } 29 } 30 ................ 31 ................为了保存封装性很多操作都需要加入AddHandler、RemoveHandler 这些相似的方法代码这未免令人感到厌烦。为了进一步简化操作事件这个概念应运而生。4.2 事件的定义事件event可被视作为一种特别的委托它为委托对象隐式地建立起add_XXX、remove_XXX 两个方法用作注册与注销事件的处理方法。而且事件对应的变量成员将会被视为 private 变量外界无法超越事件所在对象直接访问它们这使事件具备良好的封装性而且免除了add_XXX、remove_XXX等繁琐的代码。1 public class EventTest 2 { 3 public delegate void MyDelegate(); 4 public event MyDelegate MyEvent; 5 }观察事件的编译过程可知在编译的时候系统为 MyEvent 事件自动建立add_MyEvent、remove_MyEvent 方法。4.3 事件的使用方式事件能通过和-两个方式注册或者注销对其处理的方法使用与-操作符的时候系统会自动调用对应的 add_XXX、remove_XXX 进行处理。值得留意在PersonManager类的Execute方法中如果 MyEvent 绑定的处理方法不为空即可使用MyEventstring引发事件。但如果在外界的 main 方法中直接使用 personManager.MyEvent (string) 来引发事件系统将引发错误报告。这正是因为事件具备了良好的封装性使外界不能超越事件所在的对象访问其变量成员。注意在事件所处的对象之外事件只能出现在-的左方。此时开发人员无须手动添加 add_XXX、remove_XXX 的方法就可实现与4.1例子中的相同功能实现了良好的封装。1 public delegate void MyDelegate(string name); 2 3 public class PersonManager 4 { 5 public event MyDelegate MyEvent; 6 7 //执行事件 8 public void Execute(string name) 9 { 10 if (MyEvent ! null) 11 MyEvent(name); 12 } 13 } 14 15 class Program 16 { 17 static void Main(string[] args) 18 { 19 PersonManager personManager new PersonManager(); 20 //绑定事件处理方法 21 personManager.MyEvent new MyDelegate(GetName); 22 personManager.Execute(Leslie); 23 Console.ReadKey(); 24 } 25 26 public static void GetName(string name) 27 { 28 Console.WriteLine(My name is name); 29 } 30 }4.4 事件处理方法的绑定在绑定事件处理方法的时候事件出现在、- 操作符的左边对应的委托对象出现在、- 操作符的右边。对应以上例子事件提供了更简单的绑定方式只需要在、- 操作符的右方写上方法名称系统就能自动辩认。1 public delegate void MyDelegate(string name); 2 3 public class PersonManager 4 { 5 public event MyDelegate MyEvent; 6 ......... 7 } 8 9 class Program 10 { 11 static void Main(string[] args) 12 { 13 PersonManager personManager new PersonManager(); 14 //绑定事件处理方法 15 personManager.MyEvent GetName; 16 ............. 17 } 18 19 public static void GetName(string name) 20 {.........} 21 }如果觉得编写 GetName 方法过于麻烦你还可以使用匿名方法绑定事件的处理。1 public delegate void MyDelegate(string name); 2 3 public class PersonManager 4 { 5 public event MyDelegate MyEvent; 6 7 //执行事件 8 public void Execute(string name) 9 { 10 if (MyEvent ! null) 11 MyEvent(name); 12 } 13 14 static void Main(string[] args) 15 { 16 PersonManager personManager new PersonManager(); 17 //使用匿名方法绑定事件的处理 18 personManager.MyEvent delegate(string name){ 19 Console.WriteLine(My name is name); 20 }; 21 personManager.Execute(Leslie); 22 Console.ReadKey(); 23 } 24 }4.5 C#控件中的事件在C#控件中存在多个的事件像Click、TextChanged、SelectIndexChanged 等等很多都是通过 EventHandler 委托绑定事件的处理方法的EventHandler 可说是C#控件中最常见的委托 。public delegate void EventHandler (Object sender, EventArgs e)EventHandler 委托并无返回值sender 代表引发事件的控件对象e 代表由该事件生成的数据 。在ASP.NET中可以直接通过btn.Clicknew EventHandler(btn_onclick) 的方式为控件绑定处理方法。1 html xmlnshttp://www.w3.org/1999/xhtml 2 head runatserver 3 title/title 4 script typetext/C# runatserver 5 protected void Page_Load(object sender, EventArgs e) 6 { 7 btn.Click new EventHandler(btn_onclick); 8 } 9 10 public void btn_onclick(object obj, EventArgs e) 11 { 12 Button btn (Button)obj; 13 Response.Write(btn.Text); 14 } 15 /script 16 /head 17 body 18 form idform1 runatserver 19 div 20 asp:Button IDbtn runatserver TextButton/ 21 /div 22 /form 23 /body 24 /html更多时候只需要在页面使用 OnClick“btn_onclick 方法在编译的时候系统就会自动对事件处理方法进行绑定。1 html xmlnshttp://www.w3.org/1999/xhtml 2 head runatserver 3 title/title 4 script typetext/C# runatserver 5 public void btn_onclick(object obj, EventArgs e) 6 { 7 Button btn (Button)obj; 8 Response.Write(btn.Text); 9 } 10 /script 11 /head 12 body 13 form idform1 runatserver 14 div 15 asp:Button IDbtn runatserver TextButton OnClickbtn_onclick/ 16 /div 17 /form 18 /body 19 /htmlEventHandler 只是 EventHandlerTEventArgs 泛型委托的一个简单例子。事实上大家可以利用 EventHandlerTEventArgs 构造出所需要的委托。public delegate void EventHandlerTEventArgs (Object sender, TEventArgs e)在EventHandlerTEventArgs中sender代表事件源e 代表派生自EventArgs类的事件参数。开发人员可以建立派生自EventArgs的类从中加入需要使用到的事件参数然后建立 EventHandlerTEventArgs 委托。下面的例子中先建立一个派生自EventArgs的类MyEventArgs作为事件参数然后在EventManager中建立事件myEvent , 通过 Execute 方法可以激发事件。最后在测试中绑定 myEvent 的处理方法 ShowMessage在ShowMessage显示myEventArgs 的事件参数 Message。1 public class MyEventArgs : EventArgs 2 { 3 private string args; 4 5 public MyEventArgs(string message) 6 { 7 args message; 8 } 9 10 public string Message 11 { 12 get { return args; } 13 set { args value; } 14 } 15 } 16 17 public class EventManager 18 { 19 public event EventHandlerMyEventArgs myEvent; 20 21 public void Execute(string message) 22 { 23 if (myEvent ! null) 24 myEvent(this, new MyEventArgs(message)); 25 } 26 } 27 28 class Program 29 { 30 static void Main(string[] args) 31 { 32 EventManager eventManager new EventManager(); 33 eventManager.myEvent new EventHandlerMyEventArgs(ShowMessage); 34 eventManager.Execute(How are you!); 35 Console.ReadKey(); 36 } 37 38 public static void ShowMessage(object obj,MyEventArgs e) 39 { 40 Console.WriteLine(e.Message); 41 } 42 }