引言

本篇文章将为你介绍一下 Delegate 的使用方式,逐渐揭开 C# 当中事件(Event)的由来,它能使处理委托类型的过程变得更加简单。
还将为您解释委托的协变与逆变,以及如何使用 Delegate 使 Observer(观察者)模式的使用变得更加简单。

目录

一、委托类型的来由

二、建立委托类

三、委托使用方式

四、深入解析事件

五、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 所输入的参数为 object 类型,其绑定方法 GetMessage 的参数也必须为 object 。否则,即使绑定方法的参数为 object 的子类,系统也无法辨认。

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     }

运行结果

回到目录

本文转自 leslies2  51CTO博客,原文链接:http://blog.51cto.com/79100812/843489

C#综合揭秘——深入分析委托与事件(上)相关推荐

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

    引言 本篇文章将为你介绍一下 Delegate 的使用方式,逐渐揭开 C# 当中事件(Event)的由来,它能使处理委托类型的过程变得更加简单. 还将为您解释委托的协变与逆变,以及如何使用 Deleg ...

  2. 一个插排引发的设计思想 (三) 委托与事件

    一个插排引发的设计思想 (一) 观察者模式 一个插排引发的设计思想 (二) 抽象类与接口 一个插排引发的设计思想 (三) 委托与事件 ...待续.... 前两篇文章循序渐进的介绍了观察者模式.抽象类和 ...

  3. [转]C#综合揭秘——细说多线程(上)

    C#综合揭秘--细说多线程(上) 引言 本文主要从线程的基础用法,CLR线程池当中工作者线程与I/O线程的开发,并行操作PLINQ等多个方面介绍多线程的开发. 其中委托的BeginInvoke方法以及 ...

  4. C#综合揭秘——细说多线程(上)

    引言 本文主要从线程的基础用法,CLR线程池当中工作者线程与I/O线程的开发,并行操作PLINQ等多个方面介绍多线程的开发. 其中委托的BeginInvoke方法以及回调函数最为常用. 而 I/O线程 ...

  5. C#编程利器之四:委托与事件(Delegate and event) (上)

    本文试图在.net Framework环境下,使用C#语言来描述委托.事件的概貌.希望本文能有助于大家理解委托.事件的概念,理解委托.事件的用途,理解它的C#实现方法,理解委托与事件为我们带来的好处. ...

  6. [转]C#综合揭秘——细说进程、应用程序域与上下文之间的关系

    引言 本文主要是介绍进程(Process).应用程序域(AppDomain)..NET上下文(Context)的概念与操作. 虽然在一般的开发当中这三者并不常用,但熟悉三者的关系,深入了解其作用,对提 ...

  7. C#笔记(二):委托与事件

    本文内容: 1.  委托定义 2.  申明委托 3. 委托的实例化 4. 委托的调用 5. 匿名方法 6. 委托程序实例  7. 事件定义 8. 事件的申明 9. 事件的调用 10. 事件订阅与移除 ...

  8. java委托事件与观察者模式_多播委托与观察者模式联合使用,以及委托与事件的区别...

    首先我们先看一下多播委托: 使用委托时,首先我们声明委托,委托语法一共有四种类型,分别时有参,无参,以及有无参数返回值. 1 public class DelegateShow //: System. ...

  9. 如鹏网 net高级技术 第二章 委托和事件(复习)

    委托 委托是一种数据类型,可以声明委托类型变量. 委托是一种可以指向方法的数据类型. 声明委托的方式:delegate返回值类型 委托类型名(参数)   比如 delegate void MyDel( ...

最新文章

  1. ADExchange2010 简单安装部署(二)
  2. 私人定制---打造属于自己的linux小系统
  3. python数据标准化1002无标题,Scikit标准化互信息学习给我错误的值
  4. java中给组合框加监听器,如何区分ActionEvent中的多个组合框
  5. java json 解析null_解析包含null的原始json数组
  6. 加速器instagram_“类似Instagram过滤器”标记肿瘤图像中的分子细节
  7. 高通的快充协议_高通发布QC5.0快充技术最高100W+功率!手机厂商私有协议更好...
  8. 我的第一篇Live Write 写的博客
  9. 云​大数据和计算技术周报(第47期)
  10. 【国产mcu填坑篇】------STC(宏晶)
  11. PS如何快速更换logo颜色
  12. 帝国cms用ajax,帝国CMS7.5二次开发之制作Ajax版登录插件(不改系统文件)
  13. 现金红包、裂变红包、企业付款
  14. http://localhost:15672无妨访问(两种情况)
  15. python因子分析案例_python数据分析单因子分析
  16. 苹果手机自带软件删除了怎么恢复_手机照片删除了怎么恢复?莫慌,这才是正确恢复方法!_...
  17. 北航计算机学院博士开题,北航博士开题报告格式
  18. 刚刚 !SpaceX 的星际飞船,爆炸了! SpaceX 的技术到底有多强?
  19. 毕业设计之 ---- 基于Java web 的公司财务管理系统
  20. 去年今日我凭借这份文档,摇身一变成了被BAT看中的幸运儿

热门文章

  1. Java并发编程实战笔记
  2. 用Canvas创造一个太阳系
  3. Bootstrap表格内容垂直水平居中
  4. Notes:DOM的事件模拟
  5. 解决parseSdkContent failed java.lang.NullPointerException错误
  6. free -m 释放缓冲失败
  7. 点点滴滴积累——基于XMLSchema与Annotation的几种Advice的规则
  8. (转) android UI进阶之用gallery实现可滑动的Tab
  9. 图灵成立七周年——经典回顾
  10. 在使用 Elasticsearch 时要注意什么?