开篇先来扯下淡,上篇博客LZ在结尾说这篇博客会来说说C#中的事件。但是当LZ看完事件之后发现事件是以委托为基础来实现的,于是LZ就自作主张地在这篇博客中先来说说委托,还烦请各位看官见谅!!!另外关于委托推荐一篇Jimmy Zhang写的关于委托的博客(C# 中的委托和事件),叙述非常有条理,可见子阳兄的文笔不凡。

博客结构

  • 加工厂问题
  • 委托来提高加工厂效率
  • 委托的更多用法
  • 委托到底是什么

加工厂问题

假设现在我们开了一个电子设备外包工厂(就像里面有很多人跳楼的那某某康),专门负责为国际上的大公司代工生产电子设备产品。某天,加工厂收到了来自美国苹果公司的订单,苹果公司委托加工厂为他们生产一批iPhone电话机,于是加工厂就像下面这么干,效果不错还挺好,苹果公司很满意。

 1 static void Main(string[] args)
2 {
3 DigitalFactory factory = new DigitalFactory();
4 factory.MakeiPhone(200);
5 }
6
7 //摘要:
8 // 加工厂类
9 public class DigitalFactory
10 {
11 // 生产iPhone
12 public void MakeiPhone(Int32 Number)
13 {
14 Console.WriteLine("" + Number + "台iPhone已经成功生产出来了......");
15 Console.ReadKey();
16 }
17 }
18 输出:
19 200台iPhone已经成功生产出来了......

后来呢,苹果公司觉得加工厂生产的iPhone质量很好,于是又委托加工厂为他们生产一批iPad平板电脑,于是加工厂又像下面这么干了。

 1 static void Main(string[] args)
2 {
3 DigitalFactory factory = new DigitalFactory();
4 factory.MakeiPhone(200);
5 factory.MakeiPad(200);
6 }
7
8 //摘要:
9 // 加工厂类
10 public class DigitalFactory
11 {
12 // 生产iPhone
13 public void MakeiPhone(Int32 Number)
14 {
15 Console.WriteLine("" + Number + "台iPhone已经成功生产出来了......");
16 Console.Read();
17 }
18
19 // 生产iPad
20 public void MakeiPad(Int32 Number)
21 {
22 Console.WriteLine("" + Number + "台iPad已经成功生产出来了......");
23 Console.Read();
24 }
25 }
26 输出:
27 200台iPhone已经成功生产出来了......
28 200台iPad已经成功生产出来了......

这次苹果公司更满意,又继续给了代工生产iMac和iPod的两个订单,于是加工厂又傻乎乎地像下面这么干了。

 1 static void Main(string[] args)
2 {
3 DigitalFactory factory = new DigitalFactory();
4 factory.MakeiPhone(200);
5 factory.MakeiPad(200);
6 factory.MakeiMac(200);
7 factory.MakeiPod(200);
8 }
9
10 //摘要:
11 // 加工厂类
12 public class DigitalFactory
13 {
14 // 生产iPhone
15 public void MakeiPhone(Int32 Number)
16 {
17 Console.WriteLine("" + Number + "台iPhone已经成功生产出来了......");
18 Console.Read();
19 }
20
21 // 生产iPad
22 public void MakeiPad(Int32 Number)
23 {
24 Console.WriteLine("" + Number + "台iPad已经成功生产出来了......");
25 Console.Read();
26 }
27
28 // 生产iMac
29 public void MakeiMac(Int32 Number)
30 {
31 Console.WriteLine("" + Number + "台iMac已经成功生产出来了......");
32 Console.Read();
33 }
34
35 // 生产iPod
36 public void MakeiPod(Int32 Number)
37 {
38 Console.WriteLine("" + Number + "台iPod已经成功生产出来了......");
39 Console.Read();
40 }
41 }
42 输出:
43 200台iPhone已经成功生产出来了......
44 200台iPad已经成功生产出来了......
45 200台iMac已经成功生产出来了......
46 200台iPod已经成功生产出来了......

到现在加工厂才发现,随着订单越来越多,每代工生产一种产品就得新定义一个方法,那样效率太低了已经忙不过来了。所以,加工厂采用了一种新的生产模式来提高效率。

委托来提高加工厂效率

前面说到加工厂发现傻傻地为每一种代工产品提供一个发放的效率实在是太低下了,于是采用了一种新的模式。没错,大家可能都已经猜到了,这种新模式就是委托,让我们一起来看一下这新模式是如何提高效率的。

 1 //摘要:
2 // 定义一个委托类型
3 public delegate void Dele(Int32 Number);
4
5 static void Main(string[] args)
6 {
7 DigitalFactory factory = new DigitalFactory();
8 //将生产iPhone的方法传递给生产电子设备的方法
9 factory.MakeDigitals(new Apple().MakeiPhone, 200);
10 //将生产iPad的方法传递给生产电子设备的方法
11 factory.MakeDigitals(new Apple().MakeiPad, 200);
12 //将生产iMac的方法传递给生产电子设备的方法
13 factory.MakeDigitals(new Apple().MakeiMac, 200);
14 //将生产iPod的方法传递给生产电子设备的方法
15 factory.MakeDigitals(new Apple().MakeiPod, 200);
16 }
17
18 //摘要:
19 // 加工厂类
20 public class DigitalFactory
21 {
22 //定义一个通用的生产设备方法,接受一个委托变量和一个设备预生产数量作为参数
23 public void MakeDigitals(Dele dele, Int32 Number)
24 {
25 //判断委托对象是否为空,非空才执行
26 if (dele != null)
27 {
28 dele(Number);
29 }
30 }
31 }
32
33 //摘要:
34 // 苹果公司类
35 public class Apple
36 {
37 // 生产iPhone
38 public void MakeiPhone(Int32 Number)
39 {
40 Console.WriteLine("" + Number + "台iPhone已经成功生产出来了......");
41 Console.Read();
42 }
43
44 // 生产iPad
45 public void MakeiPad(Int32 Number)
46 {
47 Console.WriteLine("" + Number + "台iPad已经成功生产出来了......");
48 Console.Read();
49 }
50
51 // 生产iMac
52 public void MakeiMac(Int32 Number)
53 {
54 Console.WriteLine("" + Number + "台iMac已经成功生产出来了......");
55 Console.Read();
56 }
57
58 // 生产iPod
59 public void MakeiPod(Int32 Number)
60 {
61 Console.WriteLine("" + Number + "台iPod已经成功生产出来了......");
62 Console.Read();
63 }
64 }
65 输出:
66 200台iPhone已经成功生产出来了......
67 200台iPad已经成功生产出来了......
68 200台iMac已经成功生产出来了......
69 200台iPod已经成功生产出来了......

加工厂现在定义了一个委托(上面代码中的Dele),要求苹果公司就是按照委托的要求将产品的设计、工艺、生产流程等和生产产品所有有关的细节全整理出来。加工厂现在就只有一个通用的产品生产方法(上面代码中的MakeDigitals),加工厂不再关心电子设备的其他细节,只要按照苹果公司给的生产设备的方法(上面代码中的MakeiPhone,MakeiPad等)生产出产品即可。这样,加工厂的效率是不是就提高多了?

委托往简单的方面说,就是“把方法作为方法的参数传递给方法”。这句话是不是很绕口?意思就是假如方法A接受一个委托类型的参数,其他只要符合委托类型签名的方法就都可以当做参数传递给方法A。在上面的代码中,委托Dele签名指定的方法要获取一个Int32类型的参数,并且返回值为void。苹果公司定义的四个方法MakeiPhone,MakeiPad,MakeiMac和MakeiPod的签名都符合委托类型Dele指定的方法的签名,所以这四个方法才能被传递给接受Dele类型作为参数的MakeDigitals方法。然后在MakieDigitals方法内部回调当做参数传递进来的方法,执行生产电子设备的逻辑。

让我们来看一看c#中委托的定义:

1 //摘要:
2 // 定义一个委托类型
3 public delegate void Dele(Int32 Number);

C#通过delegate关键字来声明委托,同时要指定委托的返回类型(此处为void)和参数(此处为Int32类型的Number),这样委托就声明好了。定义好的委托就相当于一个“类型”。下面就来看一下接受委托类型作为参数的方法的定义:

1 //定义一个通用的生产设备方法,接受一个委托变量和一个设备预生产数量作为参数
2 public void MakeDigitals(Dele dele, Int32 Number)
3 {
4 //判断委托对象是否为空,非空才执行
5 if (dele != null)
6 {
7 dele(Number);
8 }
9 }

MakeDigitals方法接受一个Dele类型的委托和一个32位整数作为参数(把Dele想象成String就很好理解了,委托让我们也过了一个定义“类型”的瘾,哈哈)。只要符合Dele指定签名的方法就都可以作为“参数”传递给MakeDigitals方法了。下面就是调用MakeDigitals方法的代码:

1 DigitalFactory factory = new DigitalFactory();
2 //将生产iPhone的方法传递给生产电子设备的方法
3 factory.MakeDigitals(new Apple().MakeiPhone, 200);

看到这是不是觉得委托很简单呢?其实委托还可以帮助我们完成更多的事!

委托的更多用法

在加工厂的代码中我们只是使用委托回调了实例方法,其实委托还可以回调静态方法,还可以通过委托链一次性调用多个方法。下面我们就还是通过加工厂的代码来试探一下委托这位兄台都有哪些本事!

  • 委托调用静态方法

加工厂的例子中我们全部回调的是实例方法,下面我们还是通过加工厂来调用一下静态方法:

 1 //摘要:
2 // 定义一个委托类型
3 public delegate void Dele(Int32 Number);
4
5 static void Main(string[] args)
6 {
7 DigitalFactory factory = new DigitalFactory();
8 //将生产iPhone的方法传递给生产电子设备的方法,此时MakeiPhone为静态方法
9 factory.MakeDigitals(Apple.MakeiPhone, 200);
10 }
11
12 //摘要:
13 // 加工厂类
14 public class DigitalFactory
15 {
16 //定义一个通用的生产设备方法,接受一个委托变量和一个设备预生产数量作为参数
17 public void MakeDigitals(Dele dele, Int32 Number)
18 {
19 //判断委托对象是否为空,非空才执行
20 if (dele != null)
21 {
22 dele(Number);
23 }
24 }
25 }
26
27 //摘要:
28 // 苹果公司类
29 public class Apple
30 {
31 // 生产iPhone,静态方法
32 public static void MakeiPhone(Int32 Number)
33 {
34 Console.WriteLine("" + Number + "台iPhone已经成功生产出来了......");
35 Console.Read();
36 }
37 }
38 输出:
39 200台iPhone已经成功生产出来了......

通过委托回调静态方法和回调实例方法类似,按照传统的静态方法调用方式调用就行了。

  • 通过委托链调用多个方法

CLR通过委托链在很大程度上帮助我们减少了新建委托类型对象的数量,只要是符合委托类型签名规则的方法,都可以加入到委托类型实例的委托链中。CLR会在调用委托类型实例的代码处循环去调用委托链            中的方法,我们一起来看一下委托链的使用:

 1  //省略Dele、Apple和DigitalFactory的定义代码
2
3 static void Main(string[] args)
4 {
5 DigitalFactory factory = new DigitalFactory();
6 //新建一个Dele类型的委托对象,并且把MakeiPhone方法包装到委托对象里面
7 Dele del1 = new Dele(Apple.MakeiPhone);
8 //将MakeiPad方法加入委托对象的委托链
9 del1 += new Apple().MakeiPad;
10 //将MakeiMac方法加入委托对象的委托链
11 del1 += new Apple().MakeiMac;
12 //将MakeiPod方法加入委托对象的委托链
13 del1 += new Apple().MakeiPod;
14 factory.MakeDigitals(del1, 200);
15 }
16 输出:
17 200台iPhone已经成功生产出来了......
18 200台iPad已经成功生产出来了......
19 200台iMac已经成功生产出来了......
20 200台iPod已经成功生产出来了......

可以看到在上面的代码中,我们新建了一个Dele类型的委托对象(新建时包装了对Apple.MakeiPhone方法的引用),然后通过 += 操作符将其余三个方法的引用加入到了del1的委托链中,最后通过和加工厂代码相同的方式调用委托,输出了同样的结果。

  • 委托与Lambda表达式

将符合委托类型方法签名的方法定义好,然后再通过委托回调这些方法固然很有用,绝大多数时候我们也这么干,效果也还不错。但是在某些情况下定义的方法就被回调一两次,这太对不起我们辛辛苦苦地定           义方法了,Microsoft于是急我们之所急为我们提供了简便方法,真是大好人呐!!!下面我们来看看大好人是怎么对我们好的:

 1 //省略Dele、Apple和DigitalFactory的定义代码
2
3 static void Main(string[] args)
4 {
5 DigitalFactory factory = new DigitalFactory();
6 factory.MakeDigitals(obj =>
7 {
8 Console.WriteLine(obj + "台iPhone已经成功生产出来了......");
9 Console.ReadKey();
10 }, 200);
11 }
12 输出:
13 200台iPhone已经成功生产出来了......

上面我们就没定义MakeiPhone()方法,把它的实现内联进了代码里面。=>的左边就是方法需要的参数,如果方法需要多个参数就需要用括号括起来并且用逗号隔开。=>右边是方法的主体,多行语句也需要用大括号括起来。如果委托预期一个返回值,直接在内联代码里面加入return语句就行了。

另外,在使用Lambda表达式内联进代码的方式时,Lambda表达式还可以访问类的内部成员,像下面这样:

 1 //省略Dele、Apple和DigitalFactory的定义代码
2
3 static void Main(string[] args)
4 {
5 String LambdaDesc = "这是使用Lambda表达式实现的委托调用:";
6 DigitalFactory factory = new DigitalFactory();
7 factory.MakeDigitals(obj =>
8 {
9 Console.Write(LambdaDesc);
10 Console.WriteLine(obj + "台iPhone已经成功生产出来了......");
11 Console.ReadKey();
12 }, 200);
13 }
14 输出:
15 这是使用Lambda表达式实现的委托调用:200台iPhone已经成功生产出来了......

在Lambda表达式的内部,我们访问了属于Main方法的LambdaDesc变量。值得注意的是此时的MakeDigitals是实例方法,如果MakeDigitals是静态方法那么就只能访问静态成员,而不能访问此处的LambdaDesc。

另外:FCL已经为我们定义了大部分一般情况下需要的委托类型,例如:System.Func<out TResult>、System.Predicate<in T>、System.Action<>等等。这些委托类型能够满足绝大部分我们日常编码中的需要。

委托到底是什么

前面我们看过了委托的各种用法,那么委托为什么能够回调方法?委托到底是怎么实现的呢?下面我们就来看看委托究竟是个什么东西!

首先,我们使用ILDasm.exe来查看生成的程序集,看看编译器生成了什么IL,编译器生成的IL如下:

通过上面的图片我们看到,编译器把Dele类型的委托编译成了一个类,这个类继承自System.MulticastDelete,同时它还有一个无返回值的构造函数,以及BeginInvoke、EndInvoke和Invoke三个方法。到这里就真相大白了,其实委托是一个类。它的完整定义就像下面这样:

 1 public class Dele : System.MulticastDelegate
2 {
3 //构造器
4 public Dele(Object object,IntPtr method);
5
6 public virtual void Invoke(Int32 value);
7
8 //以下两个方法实现了对方法的回调
9 public virtual IAsyncResult BeginInvoke(Int32 value,AsyncCallback callback,Object object);
10
11 public virtual IAsyncResult EndInvoke(IAsyncResult result);
12 }

所有的委托类型都派生自System.MulticastDelegate,System.MulticastDelegate又派生自System.Delegate,而System.Delegate最终派生自所有类型的基类:System.Object。因为委托是类,所以只要能够定义类的地方就都可以定义委托,下面我们先来看一下System.MulticastDelegate中三个非常重要的非公共字段_target、_methodPtr和_invocationList:

  • _target:从定义就可以看出,这个字段引用的是回调方法要操作的对象,也就是定义回调方法的类型的实例,在加工厂的代码里面,_target引用的就是Apple的实例。值得注意的是如果委托对象包装的是一个静态方法,那么_target就为null。
  • _methodPtr:代表的是一个整数值,CLR用这个整数值来标识要回调的方法。
  • _invocationList:引用的是一个委托数组,当委托对象只包装了一个方法时,该字段的值为null。如果委托对象包装了一组方法,该字段就引用一个委托数组,就是我们前面使用的委托链的实现。

因为上面的三个字段都是MulticastDelegate类的非公共字段,所以是不能访问的,但是我们可以访问Delegate的Target和Method属性,功能和_target以及_methodPtr一样。我们还是通过加工厂的代码来看一下:

 1 public class DigitalFactory
2 {
3 //定义一个通用的生产设备方法,接受一个委托变量和一个设备预生产数量作为参数
4 public void MakeDigitals(Dele dele, Int32 Number)
5 {
6 //判断委托对象是否为空,非空才执行
7 if (dele != null)
8 {
9 dele.Invoke(Number);
10 Console.WriteLine("Target:" + dele.Target + ",Method:" + dele.Method.Name);
11 Console.ReadKey();
12 }
13 }
14 }
15 输出:
16 200台iPhone已经成功生产出来了......
17 Target:AllChapters.Program+Apple,Method:MakeiPhone

大话C#之委托的更多相关文章

  1. 大话设计模式--委托--IOS

    最近看了一些关于IOS委托的文章,看完之后,感觉不大好. 引文: 委托delegation是一种简单但是功能强大的设计模式,它的功能是程序中一个对象代表另一个对象,或者一个对象与另外一个对象协同工作. ...

  2. <大话设计模式>笔记

    读完了<大话设计模式>这本书,收获很多,对程序设计有了很多新的理解.将每章模式的大概要点做了些笔记以备查阅,一些设计模式书读完也对其了解得不是很透彻,需要以后在实践中来不断地加深理解吧.读 ...

  3. C#学习笔记(34)——委托传值(回忆版)

    说明(2018-4-6 20:31:03): 1. 昨天晚上看三层,看完第一天的最后一节,会员的修改和增加,感觉欲仙欲死,果断关机睡觉. 2. 上午搞了半天哈利波特的原版书epub的下载,结果都没发现 ...

  4. 大话AJAX原理

    大话AJAX原理 一.什么是Ajax Ajax(Asynchronous JavaScript and XML的缩写)是一种异步请求数据的web开发技术,对于改善用户的体验和页面性能很有帮助.简单地说 ...

  5. 大话设计模式--观察者模式 Observer -- C++ 实现实例

    大话设计模式--1.观察者模式: 定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象,这个主题对象在状态发生变化时,会通知所有的 观察者对象,使他们能够自动更新自己. 使用场合: 当一 ...

  6. HTML 事件(三) 事件流与事件委托

    本篇主要介绍HTML DOM中的事件流和事件委托. 其他事件文章 1. HTML 事件(一) 事件的介绍 2. HTML 事件(二) 事件的注册与注销 3. HTML 事件(三) 事件流与事件委托 4 ...

  7. C#基础篇 - 理解委托和事件

    1.委托 委托类似于C++中的函数指针(一个指向内存位置的指针).委托是C#中类型安全的,可以订阅一个或多个具有相同签名方法的函数指针.简单理解,委托是一种可以把函数当做参数传递的类型.很多情况下,某 ...

  8. [.NET] C# 知识回顾 - 委托 delegate (续)

    C# 知识回顾 - 委托 delegate (续) [博主]反骨仔 [原文]http://www.cnblogs.com/liqingwen/p/6046171.html 序 上篇<C# 知识回 ...

  9. [C#] C# 知识回顾 - 委托 delegate

    C# 知识回顾 - 委托 delegate [博主]反骨仔 [原文]http://www.cnblogs.com/liqingwen/p/6031892.html 目录 What's 委托 委托的属性 ...

随机推荐

  1. SpringCloud入门之Spring Boot多环境配置切换指南

    在 spring boot 中,有两种配置文件,一种是application.properties,另一种是application.yml,两种都可以配置spring boot 项目中的一些变量的定义 ...

  2. ES6 系列之私有变量的实现

    前言 在阅读 <ECMAScript 6 入门>的时候,零散的看到有私有变量的实现,所以在此总结一篇. 1. 约定 实现 class Example { constructor() { t ...

  3. MySQL中间件之ProxySQL(12):禁止多路路由

    返回ProxySQL系列文章:http://www.cnblogs.com/f-ck-need-u/p/7586194.html 1.multiplexing multiplexing,作用是将语句分 ...

  4. Web笔记(一) Web 简介与开发环境搭建

    Web应用程序的工作原理 大多数的Web应用程序结构都是采用最为流行的B/S软件开发体系结构,将Web应用程序部署在Web服务器上,只要Web服务器启动,用户就可以通过客户端浏览器发送HTTP请求到W ...

  5. shell编程基础(五): 正则表达式及其使用

    正则表达式 1.前情提要 以前我们用grep在一个文件中找出包含某些字符串的行,比如在头文件中找出一个宏定义.其实grep还可以找出符合某个模式(Pattern)的一类字符串.例如找出所有符合xxxx ...

  6. 玩儿虫那些事(四)—— 使用curl

    目录 一.爬一个简单的网站 二.模拟登录新浪 三.各种请求的发送 四.使用curl 五.模拟登录QQ空间 六.selenium的使用 七.phantomjs的使用 八.开源框架webmagic 九.开 ...

  7. Spring Boot入门(11)实现文件下载功能

      在这篇博客中,我们将展示如何在Spring Boot中实现文件的下载功能.   还是遵循笔者写博客的一贯风格,简单又不失详细,实用又能让你学会.   本次建立的Spring Boot项目的主要功能 ...

  8. Java8 默认方法

    概述 Java8新增了接口的默认方法.使用default关键字. 默认方法就是接口可以有实现方法,而且不需要实现类来实现其方法.相对于JDK1.8之前的接口来说,新增了可以接口中实现方法. 可以说在接 ...

  9. input 属性为 number,maxlength不起作用如何解决?

    <input type="text"  maxlength="5" />   效果ok, 当 <input type="number ...

  10. MessageChannel 消息通道

    一.初识 MessageChannel 对象 通过构造函数 MessageChannel() 可以创建一个消息通道,实例化的对象会继承两个属性:port1 和 port2 port1 和 port2 ...