深入分析委托与事件

 

C#综合揭秘——深入分析委托与事件

引言

本篇文章将为你介绍一下 Delegate 的使用方式,逐渐揭开 C# 当中事件(Event)的由来,它能使处理委托类型的过程变得更加简单。 还将为您解释委托的协变与逆变,以及如何使用 Delegate 使 Observer(观察者)模式的使用变得更加简单。 在事件的介绍上,会讲述事件的使用方式,并以ASP.NET的用户控件为例子,介绍一下自定义事件的使用。 最后一节,将介绍Predicate<T>、Action<T>、Func<T,TResult>多种泛型委托的使用和Lambda的发展过程与其使用方式。 因为时间仓促,文中有错误的地方敬请点评。

 

 

目录

一、委托类型的来由

二、建立委托类

三、委托使用方式

四、深入解析事件

五、Lambda 表达式

 

 

 

一、委托类型的来由

记得在使用C语言的年代,整个项目中都充满着针指的身影,那时候流行使用函数指针来创建回调函数,使用回调可以把函数回调给程序中的另一个函数。但函数指针只是简单地把地址指向另一个函数,并不能传递其他额外信息。 在.NET中,在大部分时间里都没有指针的身影,因为指针被封闭在内部函数当中。可是回调函数却依然存在,它是以委托的方式来完成的。委托可以被视为一个更高级的指针,它不仅仅能把地址指向另一个函数,而且还能传递参数,返回值等多个信息。系统还为委托对象自动生成了同步、异步的调用方式,开发人员使用 BeginInvoke、EndInvoke 方法就可以抛开 Thread 而直接使用多线程调用 。

回到目录

 

二、建立委托类

使用delegate就可以直接建立任何名称的委托类型,当进行系统编译时,系统就会自动生成此类型。您可以使用delegate void MyDelegate() 方式建立一个委托类,并使用ILDASM.exe观察其成员。由ILDASM.exe 中可以看到,它继承了System.MulticastDelegate类,并自动生成BeginInvoke、EndInvoke、Invoke 等三个常用方法。

Invoke 方法是用于同步调用委托对象的对应方法,而BeginInvoke、EndInvoke是用于以异步方式调用对应方法的。 对于异步调用的使用方式,可以参考:C#综合揭秘——细说多线程

复制代码
1      public class MyDelegate:MulticastDelegate
2      {
3          //同步调用委托方法 4 public virtual void Invoke(); 5 //异步调用委托方法 6 public virtual IAsyncResult BeginInvoke(AsyncCallback callback,object state); 7 public virtual void EndInvoke(IAsyncResult result); 8 }
复制代码

MulticastDelegate是System.Delegate的子类,它是一个特殊类,编译器和其他工具可以从此类派生,但是自定义类不能显式地从此类进行派生。它支持多路广播委托,并拥有一个带有链接的委托列表,在调用多路广播委托时,系统将按照调用列表中的委托出现顺序来同步调用这些委托。

MulticastDelegate具有两个常用属性:Method、Target。其中Method 用于获取委托所表示的方法Target 用于获取当前调用的类实例。

MulticastDelegate有以下几个常用方法:

方法名称说明
 Clone  创建委托的浅表副本。
 GetInvocationList  按照调用顺序返回此多路广播委托的调用列表。
 GetMethodImpl  返回由当前的 MulticastDelegate 表示的静态方法。
 GetObjectData  用序列化该实例所需的所有数据填充 SerializationInfo 对象。
 MemberwiseClone  创建当前 Object 的浅表副本。
 RemoveImpl  调用列表中移除与指定委托相等的元素

MulticastDelegate与Delegate给委托对象建立了强大的支持,下面向各位详细介绍一下委托的使用方式。

回到目录

 

三、委托使用方式

3.1 简单的委托

当建立委托对象时,委托的参数类型必须与委托方法相对应。只要向建立委托对象的构造函数中输入方法名称example.Method,委托就会直接绑定此方法。使用myDelegate.Invoke(string message),就能显式调用委托方法。但在实际的操作中,我们无须用到 Invoke 方法,而只要直接使用myDelegate(string message),就能调用委托方法。

复制代码
 1     class Program  2     {  3         delegate void MyDelegate(string message);  4  5 public class Example  6 {  7 public void Method(string message)  8 {  9 MessageBox.Show(message); 10 } 11 } 12 13 static void Main(string[] args) 14 { 15 Example example=new Example(); 16 MyDelegate myDelegate=new MyDelegate(example.Method); 17 myDelegate("Hello World"); 18 Console.ReadKey(); 19 } 20 }
复制代码

 

3.2 带返回值的委托

当建立委托对象时,委托的返回值必须与委托方法相对应。使用下面的例子,方法将返回 “Hello Leslie” 。

复制代码
 1     class Program
 2     {
 3         delegate string MyDelegate(string message);  4  5 public class Example  6 {  7 public string Method(string name)  8 {  9 return "Hello " + name; 10 } 11 } 12 13 static void Main(string[] args) 14 { 15 Example example=new Example(); 16 //绑定委托方法 17 MyDelegate myDelegate=new MyDelegate(example.Method); 18 //调用委托,获取返回值 19 string message = myDelegate("Leslie"); 20 Console.WriteLine(message); 21 Console.ReadKey(); 22 } 23 }
复制代码

 

3.3 多路广播委托

在第二节前曾经提过,委托类继承于MulticastDelegate,这使委托对象支持多路广播,即委托对象可以绑定多个方法。当输入参数后,每个方法会按顺序进行迭代处理,并返回最后一个方法的计算结果。 下面的例子中,Price 类中有两个计算方法,Ordinary 按普通的9.5折计算,Favourable 按优惠价 8.5 折计算。委托同时绑定了这两个方法,在输入参数100以后,Ordinary、Favourable这两个方法将按顺序迭代执行下去,最后返回 Favourable 方法的计算结果 85。

复制代码
 1         delegate double MyDelegate(double message);  2   3 public class Price  4 {  5 public double Ordinary(double price)  6 {  7 double price1 = 0.95 * price;  8 Console.WriteLine("Ordinary Price : "+price1);  9 return price1; 10 } 11 12 public double Favourable(double price) 13 { 14 double price1 = 0.85 * price; 15 Console.WriteLine("Favourable Price : " + price1); 16 return price1; 17 } 18 19 static void Main(string[] args) 20 { 21 Price price = new Price(); 22 //绑定Ordinary方法 23 MyDelegate myDelegate = new MyDelegate(price.Ordinary); 24 //绑定Favourable方法 25 myDelegate += new MyDelegate(price.Favourable); 26 //调用委托 27 Console.WriteLine("Current Price : " + myDelegate(100)); 28 Console.ReadKey(); 29 } 30 }
复制代码

运行结果

3.4 浅谈Observer模式

回顾一下简单的 Observer 模式,它使用一对多的方式,可以让多个观察者同时关注同一个事物,并作出不同的响应。 例如下面的例子,Manager的底薪为基本工资的1.5倍,Assistant的底薪为基本工资的1.2倍。WageManager类的RegisterWorker方法与RemoveWorker方法可以用于注册和注销观察者,最后执行Execute方法可以对多个已注册的观察者同时输入参数。

 

 

复制代码
 1     public class WageManager
 2     {
 3         IList<Worker> workerList = new List<Worker>();  4  5 public void RegisterWorker(Worker worker)  6 {  7 workerList.Add(worker);  8 }  9 10 public void RemoveWorker(Worker worker) 11 { 12 workerList.Remove(worker); 13 } 14 15 public void Excute(double basicWages) 16 { 17 if (workerList.Count != 0) 18 foreach (var worker in workerList) 19 worker.GetWages(basicWages); 20 } 21 22 static void Main(string[] args) 23 { 24 WageManager wageManager = new WageManager(); 25 //注册观察者 26 wageManager.RegisterWorker(new Manager()); 27 wageManager.RegisterWorker(new Assistant()); 28 //同时输入底薪3000元,分别进行计算 29 wageManager.Excute(3000); 30 31 Console.ReadKey(); 32 } 33 } 34 35 public abstract class Worker 36 { 37 public abstract double GetWages(double basicWages); 38 } 39 40 public class Manager:Worker 41 { 42 //Manager实际工资为底薪1.5倍 43 public override double GetWages(double basicWages) 44 { 45 double totalWages = 1.5 * basicWages; 46 Console.WriteLine("Manager's wages is " + totalWages); 47 return totalWages; 48 } 49 } 50 51 public class Assistant : Worker 52 { 53 //Assistant实际工资为底薪的1.2倍 54 public override double GetWages(double basicWages) 55 { 56 double totalWages = 1.2 * basicWages; 57 Console.WriteLine("Assistant's wages is " + totalWages); 58 return totalWages; 59 } 60 }
复制代码

运行结果

 

开发 Observer 模式时借助委托,可以进一步简化开发的过程。由于委托对象支持多路广播,所以可以把Worker类省略。在WageManager类中建立了一个委托对象wageHandler,通过Attach与Detach方法可以分别加入或取消委托。如果观察者想对事物进行监测,只需要加入一个委托对象即可。记得在第二节曾经提过,委托的GetInvodationList方法能获取多路广播委托列表,在Execute方法中,就是通过去多路广播委托列表去判断所绑定的委托数量是否为0。

复制代码
 1         public delegate double Handler(double basicWages);  2  3 public class Manager  4 {  5 public double GetWages(double basicWages)  6 {  7 double totalWages=1.5 * basicWages;  8 Console.WriteLine("Manager's wages is : " + totalWages);  9 return totalWages; 10 } 11 } 12 13 public class Assistant 14 { 15 public double GetWages(double basicWages) 16 { 17 double totalWages = 1.2 * basicWages; 18 Console.WriteLine("Assistant's wages is : " + totalWages); 19 return totalWages; 20 } 21 } 22 23 public class WageManager 24 { 25 private Handler wageHandler; 26 27 //加入观察者 28 public void Attach(Handler wageHandler1) 29 { 30 wageHandler += wageHandler1; 31 } 32 33 //删除观察者 34 public void Detach(Handler wageHandler1) 35 { 36 wageHandler -= wageHandler1; 37 } 38 39 //通过GetInvodationList方法获取多路广播委托列表,如果观察者数量大于0即执行方法 40 public void Execute(double basicWages) 41 { 42 if (wageHandler!=null) 43 if(wageHandler.GetInvocationList().Count() != 0) 44 wageHandler(basicWages); 45 } 46 47 static void Main(string[] args) 48 { 49 WageManager wageManager = new WageManager(); 50 //加入Manager观察者 51 Manager manager = new Manager(); 52 Handler managerHandler = new Handler(manager.GetWages); 53 wageManager.Attach(managerHandler); 54 55 //加入Assistant观察者 56 Assistant assistant = new Assistant(); 57 Handler assistantHandler = new Handler(assistant.GetWages); 58 wageManager.Attach(assistantHandler); 59 60 //同时加入底薪3000元,分别进行计算 61 wageManager.Execute(3000); 62 Console.ReadKey(); 63 } 64 }
复制代码

最后运行结果与上面的例子相同。

 

3.5 委托的协变与逆变

在 Framework 2.0 出现之前,委托协变这个概念还没有出现。此时因为委托是安全类型,它们不遵守继承的基础规则。即会这下面的情况:Manager 虽然是 Worker 的子类,但 GetWorkerHander 委托不能直接绑定 GetManager 方法,因为在委托当中它们的返回值 Manager 与 Worker 被视为完全无关的两个类型。

复制代码
 1      public class Worker
 2      {.......}
 3      public class Manager:Worker  4 {.......}  5  6 class Program  7 {  8 public delegate Worker GetWorkerHandler(int id);  9 public delegate Manager GetManagerHandler(int id); 10 11 public static Worker GetWorker(int id) 12 { 13 Worker worker = new Worker(); 14 .............. 15 return worker; 16 } 17 18 public static Manager GetManager(int id) 19 { 20 Manager manager = new Manager(); 21 .............. 22 return manager; 23 } 24 25 static void Main(string[] args) 26 { 27 GetWorkerHandler workerHandler = new GetWorkerHandler(GetWorker); 28 var worker=workerHandler(1); 29 30 GetManagerHandler managerHandler = new GetManagerHandler(GetManager); 31 var manager = managerHandler(2); 32 Console.ReadKey(); 33 } 34 }
复制代码

自从Framework 2.0 面试以后,委托协变的概念就应运而生,此时委托可以按照传统的继承规则进行转换。即 GetWorkerHandler 委托可以直接绑定 GetManager 方法。

复制代码
 1      public class Worker  2      {.......}  3      public class Manager:Worker  4 {.......}  5  6 class Program  7 {  8 public delegate Worker GetWorkerHandler(int id);  9 //在 Framework2.0 以上,委托 GetWorkerHandler 可绑定 GetWorker 与 GetManager 两个方法
10 11 public static Worker GetWorker(int id) 12 { 13 Worker worker = new Worker(); 14 return worker; 15 } 16 17 public static Manager GetManager(int id) 18 { 19 Manager manager = new Manager(); 20 return manager; 21 } 22 23 static void Main(string[] args) 24 { 25 GetWorkerHandler workerHandler = new GetWorkerHandler(GetWorker); 26 Worker worker=workerHandler(1); 27 GetWorkerHandler managerHandler = new GetWorkerHandler(GetManager); 28 Manager manager = managerHandler(2) as Manager; 29 Console.ReadKey(); 30 } 31 }
复制代码

委托逆变,是指委托方法的参数同样可以接收 “继承” 这个传统规则。像下面的例子,以 object 为参数的委托,可以接受任何 object 子类的对象作为参数。最后可以在处理方法中使用 is 对输入数据的类型进行判断,分别处理对不同的类型的对象。

复制代码
 1     class Program
 2     {
 3         public delegate void Handler(object obj);  4  5 public static void GetMessage(object message)  6 {  7 if (message is string)  8 Console.WriteLine("His name is : " + message.ToString());  9 if (message is int) 10 Console.WriteLine("His age is : " + message.ToString()); 11 } 12 13 static void Main(string[] args) 14 { 15 Handler handler = new Handler(GetMessage); 16 handler(29); 17 Console.ReadKey(); 18 } 19 }
复制代码

运行结果

注意委托与其绑定方法的参数必须一至,即当 Handler 所输入的参数为 A 类型,其绑定方法 GetMessage 的参数也必须为 A 类或者 A 的父类 。相反,当绑定方法的参数为 A 的子类,系统也无法辨认。

  3.6 泛型委托

委托逆变虽然实用,但如果都以 object 作为参数,则需要每次都对参数进行类型的判断,这不禁令人感到厌烦。 为此,泛型委托应运而生,泛型委托有着委托逆变的优点,同时利用泛型的特性,可以使一个委托绑定多个不同类型参数的方法,而且在方法中不需要使用 is 进行类型判断,从而简化了代码。

复制代码
 1     class Program  2     {  3         public delegate void Handler<T>(T obj);  4  5 public static void GetWorkerWages(Worker worker)  6 {  7 Console.WriteLine("Worker's total wages is " + worker.Wages);  8 }  9 10 public static void GetManagerWages(Manager manager) 11 { 12 Console.WriteLine("Manager's total wages is "+manager.Wages); 13 } 14 15 static void Main(string[] args) 16 { 17 Handler<Worker> workerHander = new Handler<Worker>(GetWorkerWages); 18 Worker worker = new Worker(); 19 worker.Wages = 3000; 20 workerHander(worker); 21 22 Handler<Manager> managerHandler = new Handler<Manager>(GetManagerWages); 23 Manager manager = new Manager(); 24 manager.Wages = 4500; 25 managerHandler(manager); 26 27 Console.ReadKey(); 28 } 29 }
复制代码

运行结果

回到目录

四、深入解析事件

4.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 这些相似的方法代码,这未免令人感到厌烦。 为了进一步简化操作,事件这个概念应运而生。

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 绑定的处理方法不为空,即可使用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.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.Click+=new EventHandler(btn_onclick) 的方式为控件绑定处理方法。

复制代码
 1 <html xmlns="http://www.w3.org/1999/xhtml">  2 <head runat="server">  3 <title></title>  4 <script type="text/C#" runat="server">  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 id="form1" runat="server"> 19 <div> 20 <asp:Button ID="btn" runat="server" Text="Button"/> 21 </div> 22 </form> 23 </body> 24 </html>
复制代码

更多时候,只需要在页面使用 OnClick=“btn_onclick" 方法,在编译的时候系统就会自动对事件处理方法进行绑定。

复制代码
 1 <html xmlns="http://www.w3.org/1999/xhtml">  2 <head runat="server">  3 <title></title>  4 <script type="text/C#" runat="server">  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 id="form1" runat="server"> 14 <div> 15 <asp:Button ID="btn" runat="server" Text="Button" OnClick="btn_onclick"/> 16 </div> 17 </form> 18 </body> 19 </html>
复制代码

 

EventHandler 只是 EventHandler<TEventArgs> 泛型委托的一个简单例子。事实上,大家可以利用 EventHandler<TEventArgs> 构造出所需要的委托。

public delegate void EventHandler<TEventArgs> (Object sender, TEventArgs e)

在EventHandler<TEventArgs>中,sender代表事件源,e 代表派生自EventArgs类的事件参数。开发人员可以建立派生自EventArgs的类,从中加入需要使用到的事件参数,然后建立 EventHandler<TEventArgs> 委托。

下面的例子中,先建立一个派生自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 EventHandler<MyEventArgs> 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 EventHandler<MyEventArgs>(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 }
复制代码

运行结果

 

4.6 为用户控件建立事件

在ASP.NET开发中,页面往往会出现很多类似的控件与代码,开发人员可以通过用户控件来避免重复的代码。但往往同一个用户控件,在不同的页面中需要有不同的响应。此时为用户控件建立事件,便可轻松地解决此问题。 下面例子中,在用户控件 MyControl 中建立存在一个GridView控件,GridView 控件通过 GetPersonList 方法获取数据源。在用户控件中还定义了 RowCommand 事件,在 GridView 的 GridView_RowCommand 方法中激发此事件。这样,在页面使用此控件时,开发人员就可以定义不同的方法处理 RowCommand 事件。

复制代码
 1 public class Person  2 {  3     public int ID  4     { get; set; }  5     public string Name  6 { get; set; }  7 public int Age  8 { get; set; }  9 } 10 11 <!-- 用户控件 --> 12 <%@ Control Language="C#" AutoEventWireup="true" CodeFile="MyControl.ascx.cs" Inherits="MyControl" %> 13 <script type="text/C#" runat="server"> 14  protected void Page_Load(object sender, EventArgs e) 15  { 16  GridView1.DataSource = GetPersonList(); 17  GridView1.DataBind(); 18  } 19  20  //绑定数据源 21  protected IList<Person> GetPersonList() 22  { 23  IList<Person> list = new List<Person>(); 24  Person person1 = new Person(); 25  person1.ID = 1; 26  person1.Name = "Leslie"; 27  person1.Age = 29; 28  list.Add(person1); 29  ........... 30  return list; 31  } 32  33  public event GridViewCommandEventHandler RowCommand; 34  35  protected void GridView1_RowCommand(object sender, GridViewCommandEventArgs e) 36  { 37  if (RowCommand != null) 38  RowCommand(sender, e); 39  } 40 </script> 41 <div> 42 <asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False" 43  onrowcommand="GridView1_RowCommand"> 44 <Columns> 45 <asp:BoundField DataField="ID" HeaderText="ID"/> 46 <asp:BoundField DataField="Name" HeaderText="Name"/> 47 <asp:BoundField DataField="Age" HeaderText="Age"/> 48 <asp:ButtonField CommandName="Get" Text="Select"/> 49 </Columns> 50 </asp:GridView> 51 </div> 52 53 <!-- 页面代码 --> 54 <%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default2.aspx.cs" Inherits="Default2" %> 55 <%

转载于:https://www.cnblogs.com/ruishuang208/p/4962251.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值