深入解析事件

1 事件的由来

  在介绍事件之前大家可以先看看下面的例子, 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  Computer's price is {0}!",
31                 priceManager.GetPrice()));
32             Console.WriteLine(string.Format("Invoke\n  Computer's 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  Book's price is {0}!",
39                 priceManager.GetPrice()));
40             Console.WriteLine(string.Format("Invoke\n  Book's 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, 再分别加入 AddHandler,RemoveHandler 方法对 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 这些相似的方法代码,这未免令人感到厌烦。
  为了进一步简化操作,事件这个概念应运而生。
 

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 方法。

 

3 事件的使用方式

  事件能通过+=和-=两个方式注册或者注销对其处理的方法,使用+=与-=操作符的时候,系统会自动调用对应的 add_XXX、remove_XXX 进行处理。
  值得留意,在PersonManager类的Execute方法中,如果 MyEvent 绑定的处理方法不为空,即可使用MyEvent(string)引发事件。但如果在外界的 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 事件处理方法的绑定

  在绑定事件处理方法的时候,事件出现在+=、-= 操作符的左边,对应的委托对象出现在+=、-= 操作符的右边。对应以上例子,事件提供了更简单的绑定方式,只需要在+=、-= 操作符的右方写上方法名称,系统就能自动辩认。

 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     }

来源:http://www.cnblogs.com/zhangyanhai/archive/2013/10/09/3359240.html
posted @ 2013-10-14 15:29  蚂蚁拉车  阅读(236)  评论(0编辑  收藏  举报