第九节:委托和事件(1)(委托的发展历史、插件式编程、多播委托)
一. 委托的发展历史和基本用法
说起委托,每个人可能都会对他有不同的理解,结合实战中委托的使用,我对其理解是:委托和类一样,是用户的一个自定义类型,委托可以有参数、有返回值,委托的关键字是delegate,委托是方法的抽象,有了委托的存在,使得方法可以作为参数传递给另一个方法,同时调用委托的时候,委托所包含的所有方法都会被实现。
委托的写法由繁琐→简洁,经历了好多过程,大致概括为:new实例化传递方法→直接等于方法名→new实例化中传递匿名方法→省略匿名方法关键字→可以去掉大括号和分号 等等。
代码如下:
1 public class MyDelegate2 {3 //1. 委托的声明4 public delegate void NoReturnNoPara();5 public delegate int WithReturnNoPara();6 public delegate void NoReturnWithPara(int id, string name);7 public delegate MyDelegate WithReturnWithPara(DateTime time);8 9
10 //2. 委托的使用(在show方法中调用)
11 public void Show()
12 {
13 //以“有参无返回值委托”为例,介绍委托的各种用法
14 //2.1 用法一
15 {
16 NoReturnWithPara methord = new NoReturnWithPara(this.Test1);
17 methord.Invoke(1, "唐马儒1");
18 }
19 //2.2 用法二
20 {
21 NoReturnWithPara methord = this.Test1;
22 methord.Invoke(2, "唐马儒2");
23 }
24 //2.3 用法三 DotNet 2.0 时代
25 {
26 NoReturnWithPara methord = new NoReturnWithPara
27 (
28 delegate(int id, string name)
29 {
30 Console.WriteLine("{0} {1}", id, name);
31 }
32 );
33 methord.Invoke(3, "唐马儒3");
34 }
35 //2.4 用法四 DotNet 3.0 时代
36 {
37 NoReturnWithPara methord = new NoReturnWithPara
38 (
39 (int id, string name) =>
40 {
41 Console.WriteLine("{0} {1}", id, name);
42 }
43 );
44 methord.Invoke(4, "唐马儒4");
45 }
46 //2.5 用法五 委托约束
47 {
48 NoReturnWithPara methord = new NoReturnWithPara
49 (
50 (id,name) =>
51 {
52 Console.WriteLine("{0} {1}", id, name);
53 }
54 );
55 methord.Invoke(5, "唐马儒5");
56 }
57 //2.6 用法六 (如果方法体只有一行,可以去掉大括号和分号)
58 {
59 NoReturnWithPara methord = new NoReturnWithPara((id, name) => Console.WriteLine("{0} {1}", id, name));
60 methord.Invoke(6, "唐马儒6");
61 }
62 //2.7 用法七
63 {
64 NoReturnWithPara methord = (id, name) => Console.WriteLine("{0} {1}", id, name);
65 methord.Invoke(7, "唐马儒7");
66 methord(7, "唐马儒7");
67 }
68 //2.8 用法八
69 {
70 //Func<int, bool> methord = (x) =>
71 // {
72 // return x > 6;
73 // };
74 //等价于(原理,当只有一个参数的时候,可以省略参数的小括号,当方法体只有一行的时候,可以省略大括号和分号,即lambda形式)
75 Func<int, bool> methord = x => x > 6;
76 Console.WriteLine(methord.Invoke(8));
77 }
78
79 }
80 private void Test1(int id, string name)
81 {
82 Console.WriteLine("{0} {1}", id, name);
83 }
84
85 private void Test2()
86 {
87 Console.WriteLine("DoNothing");
88 }
89
90 private void Test3()
91 {
92 Console.WriteLine("DoNothing");
93 }
94 }
二. 解耦、插件式编程
1. 背景:有一个CarFactory工厂类,可以建造自然吸气发动机、涡轮增压发动机、电动发动机,至于要建造哪个发动机,有多种处理方案。
方案(一):通过传递不同的参数来建造不同的发动机
原理:传递一个参数,根据参数类型来执行不同的逻辑
缺点:如果增加新的发动机类型,需要修改BuildEngine方法中的内部逻辑,不符合开闭原则
代码如下:
1 /// <summary>2 /// 这里建一个Car工厂,用来建造三种不同类型的发动机3 /// </summary>4 public class CarFactory5 {6 /// <summary>7 /// 方案一:通过传递不同的参数来建造不同的发动机8 /// 原理:传递一个参数,根据参数类型来执行不同的逻辑9 /// 缺点:如果增加新的发动机类型,需要修改BuildEngine方法中的内部逻辑,不符合开闭原则
10 /// </summary>
11 /// <param name="type">参数类型</param>
12 public static void BuildEngine(EngineType type)
13 {
14 if (type == EngineType.NaturalInspiration)
15 {
16 Console.WriteLine("建造自然吸气发动机");
17 }
18 else if (type == EngineType.Turbo)
19 {
20 Console.WriteLine("建造涡轮增压发动机");
21 }
22 else if (type == EngineType.Electric)
23 {
24 Console.WriteLine("建造电动发动机");
25 }
26 }
27
28 /// <summary>
29 /// 发动机类型的枚举类
30 /// </summary>
31 public enum EngineType
32 {
33 NaturalInspiration = 0, //自然吸气
34 Turbo = 1, //涡轮增压
35 Electric = 2 //电动
36 }
37 }
调用:
1 //1.传统的方式,通过参数类型来区分
2 Console.WriteLine("--------------------------1.传统的方式,通过参数类型来区分------------------------------------");
3 CarFactory.BuildEngine(EngineType.NaturalInspiration);
4 CarFactory.BuildEngine(EngineType.Turbo);
5 CarFactory.BuildEngine(EngineType.Electric);
结果:
方案(二):通过传递委托来建造不同的发动机
原理:传递一个逻辑给我,我去执行
优点:如果增加新的发动机,只需要单独新增对应建造发动机的方法即可,不需要改变BuildEngine2的内部逻辑,符合开闭原则
代码如下:
1 public class CarFactory2 {3 /// <summary>4 /// 方案二:通过传递委托来建造不同的发动机5 /// 原理:传递一个逻辑给我,我去执行6 /// 优点:如果增加新的发动机,只需要单独新增对应建造发动机的方法即可,不需要改变BuildEngine2的内部逻辑,符合开闭原则7 /// </summary>8 public static void BuildEngine2(BuildEngineDel be)9 {
10 be.Invoke();
11 }
12 //声明一个无参数的委托
13 public delegate void BuildEngineDel();
14 //下面三个是建造不同发动机的方法
15 public static void BuildNatural()
16 {
17 Console.WriteLine("建造自然吸气发动机");
18 }
19 public static void BuildTurbo()
20 {
21 Console.WriteLine("建造涡轮增压发动机");
22 }
23 public static void BulidElectric()
24 {
25 Console.WriteLine("建造电动发动机");
26 }
27
28 }
29 }
调用:
1 //2.将委托当做参数,进行解耦,实现插件式编程(案例一)
2 Console.WriteLine("--------------------------2.将委托当做参数,进行解耦,实现插件式编程(案例一)------------------------------------");
3 //将方法赋给委托,将委托当做参数传递给新方法
4 CarFactory.BuildEngineDel be1 = CarFactory.BuildNatural;
5 CarFactory.BuildEngine2(be1);
6 CarFactory.BuildEngineDel be2 = CarFactory.BuildTurbo;
7 CarFactory.BuildEngine2(be2);
8 CarFactory.BuildEngineDel be3 = CarFactory.BulidElectric;
9 CarFactory.BuildEngine2(be3);
结果:
2. 背景:现在有一个类,该类要实现对一个int类型的数组中的每个数进行加倍、平方、立方,并输出
传统解决方案一:在该类中声明多个方法,分别是加倍、平方、立方的方法
传统解决方案二:在该类中声明一个万能方法,通过传递不同的参数类型来区分是执行加倍还是平方或者立方操作
方案三:声明一个万能方法,传递一个委托进来,相当于传递了一个业务逻辑进行,在该方法里只需要执行即可
代码如下:
1 public class Calculator2 {3 //解决方案三:声明一个万能方法,传递一个委托进来,相当于传递了一个业务逻辑进行,在该方法里只需要执行即可4 /// <summary>5 /// 万能方法6 /// </summary>7 /// <param name="arrs">int类型的数组 </param>8 /// <param name="mydel">系统自带的委托(也可以自定义委托),<int,int>代表:参数和返回值均为int类型</param>9 public static void MySpecMethord(int[] arrs, Func<int,int> mydel)
10 {
11 for (int i = 0; i < arrs.Length; i++)
12 {
13 arrs[i] = mydel(arrs[i]);
14 //arrs[i] = mydel.Invoke(arrs[i]); //等价于上面那句
15 Console.WriteLine(arrs[i]);
16 }
17 }
18
19 }
三种调用形式:声明Func委托把方法赋值给它、直接把方法名传递进去、传递lambda方法。
1 Console.WriteLine("--------------------------3.将委托当做参数,进行解耦,实现插件式编程(案例二)------------------------------------");2 int[] arr = { 1, 2, 3, 4, 5 };3 //调用形式一:4 Console.WriteLine("--------------------------调用形式一------------------------------------");5 Func<int, int> mydel = t1;6 Calculator.MySpecMethord(arr, mydel);7 //调用形式二:8 Console.WriteLine("--------------------------调用形式二------------------------------------");9 Calculator.MySpecMethord(arr, t2);
10 //调用形式三:
11 Console.WriteLine("--------------------------调用形式三------------------------------------");
12 Calculator.MySpecMethord(arr, x => x * 2);
13 /// <summary>
14 /// 加倍方法
15 /// </summary>
16 /// <param name="x"></param>
17 /// <returns></returns>
18 public static int t1(int x)
19 {
20 return x * 2;
21 }
22 /// <summary>
23 /// 乘方方法
24 /// </summary>
25 /// <param name="x"></param>
26 /// <returns></returns>
27 public static int t2(int x)
28 {
29 return x * x;
30 }
31 /// <summary>
32 /// 立方方法
33 /// </summary>
34 /// <param name="x"></param>
35 /// <returns></returns>
36 public static int t3(int x)
37 {
38 return x * x * x;
39 }
结果:
三. 多播委托
1. 含义:所有的委托的实例都有多播的功能,自定义委托和内置委托都有,可以通过+=和-=给委托增加和删掉不同的方法。
下面的代码中分别介绍四个不同类别的方法赋值给普通委托和多播委托的形式。
1 public class myMultiDelegate2 {3 //自定义一个没有参数没有返回值的委托4 public delegate void NoReturnNoPara();5 6 /// <summary>7 /// 测试委托常规用法8 /// </summary>9 public void show1()
10 {
11 Student student = new Student();
12 //委托的普通用法,分别调用当前类的实例方法、当前类的静态方法、其他类的实例方法、其他类的静态方法
13 //声明4个委托
14 NoReturnNoPara n1 = this.DoNothing; //调用当前类的实例方法
15 NoReturnNoPara n2 = myMultiDelegate.DoNothingStatic; //调用当前类的静态方法
16 NoReturnNoPara n3 = student.Study;
17 NoReturnNoPara n4 = Student.StudyAdvanced;
18
19 //执行
20 n1.Invoke();
21 n2.Invoke();
22 n3.Invoke();
23 n4.Invoke();
24 }
25 /// <summary>
26 /// 测试多播委托的用法
27 /// </summary>
28 public void show2()
29 {
30 //所有的委托的实例都有多播的功能,自定义委托和内置委托都有
31 NoReturnNoPara n1 = this.DoNothing;
32 //+=就是把多个方法按顺序排成列表,invoke时按顺序执行
33 n1 += myMultiDelegate.DoNothingStatic;
34 n1 += Student.StudyAdvanced;
35 n1 += this.DoNothing;
36 n1 += this.DoNothing;
37 n1 += this.DoNothing;
38 n1 += this.DoNothing;
39 //-=就是从这个实例上,从后往前挨个匹配,找到第一个完全吻合的移除掉,且只移除一个,找不到不异常
40 //注意,对于委托,+= 和 -= 对null是不会报错的
41 n1 -= this.DoNothing;
42 n1.Invoke();
43 }
44
45
46 #region 两个供委托调用的测试方法
47 private void DoNothing()
48 {
49 Console.WriteLine("当前类中的实例方法");
50 }
51 private static void DoNothingStatic()
52 {
53 Console.WriteLine("当前类中的静态方法");
54 }
55 #endregion
56 }
2. 多播委托的实际使用场景。
实际注册业务场景:先查询账号是否存在(如果不存在)→向表1中插入信息→向表2中插入信息。。。。,随着业务新需求的变更,注册时需要向表3中插入数据, 过了一段时间,需求又变了,不需要向表2中添加数据了。
下述代码是一个注册流程的类。
1 public class RegisterUtils2 {3 //传统的解决方案,在一个方法里书写各种业务。4 //缺点:后期需求变更了,当需要插入更多数据的时候,只能修改该方法内部逻辑,不符合开闭原则5 6 7 //下面介绍利用多播委托进行解耦插件式开发8 public delegate void myRegisterDelegate(string userName, string userPwd);9
10 public static void myRegister(myRegisterDelegate mrd, string userName, string userPwd)
11 {
12 //对密码进行Md5加密后在进行后续操作
13 string md5userPwd = userPwd + "MD5"; //模拟Md5
14 mrd.Invoke(userName, md5userPwd);
15 }
16 /// <summary>
17 /// 查询方法
18 /// </summary>
19 public static void checkIsRegister(string userName, string userPwd)
20 {
21 Console.WriteLine("查询成功");
22 }
23 /// <summary>
24 /// 向表1中插入数据
25 /// </summary>
26 public static void writeTable1(string userName, string userPwd)
27 {
28 Console.WriteLine("向表1中插入数据{0},{1}",userName,userPwd);
29 }
30 /// <summary>
31 /// 向表2中插入数据
32 /// </summary>
33 public static void writeTable2(string userName, string userPwd)
34 {
35 Console.WriteLine("向表2中插入数据{0},{1}", userName, userPwd);
36 }
37 /// <summary>
38 /// 向表3中插入数据
39 /// </summary>
40 public static void writeTable3(string userName, string userPwd)
41 {
42 Console.WriteLine("向表3中插入数据{0},{1}", userName, userPwd);
43 }
44 }
调用
1 Console.WriteLine("--------------------------3.多播委托的案例(在注册流程中) ------------------------------------");
2 RegisterUtils.myRegisterDelegate mrd = RegisterUtils.checkIsRegister;
3 mrd += RegisterUtils.writeTable1;
4 mrd += RegisterUtils.writeTable2;
5 //需求增加了,向表3中添加数据
6 mrd += RegisterUtils.writeTable3;
7 //需求变更了,删除向表2中添加数据
8 mrd -= RegisterUtils.writeTable2;
9 RegisterUtils.myRegister(mrd, "maru", "123");
结果:
补充:在下一个章节:.Net进阶系列(7)-委托和事件(二)中,会继续介绍泛型委托、内置委托、委托的其他性质、委托和事件。
第九节:委托和事件(1)(委托的发展历史、插件式编程、多播委托)相关推荐
- C# 关于委托和事件的妙文:通过一个例子详细介绍委托和事件的作用;Observer模式简介...
委托和事件在 .Net Framework中的应用非常广泛,然而,较好地理解委托和事件对很多接触C#时间不长的人来说并不容易.它们就像是一道槛儿,过了这个槛的人,觉得真是太容易了,而没有过去的人每次见 ...
- 第十节:委托和事件(2)(泛型委托、Func和Action、事件及与委托的比较)
一. 泛型委托 所谓的泛型委托,即自定义委托的参数可以用泛型约束,同时内置委托Func和Action本身就是泛型委托. 将上一个章节中的Calculator类中的方法用自定义泛型委托重新实现一下. 1 ...
- C# 委托(Delegate) 事件(Event)应用详解
委托 和 事件在 .Net Framework中的应用非常广泛,然而,较好地理解委托和事件对很多接触C#时间不长的人来说并不容易.它们就像是一道槛儿,过了这个槛的人,觉得真是太容易了,而没有过去的人每 ...
- C#编程利器之四:委托与事件(Delegate and event) (上)
本文试图在.net Framework环境下,使用C#语言来描述委托.事件的概貌.希望本文能有助于大家理解委托.事件的概念,理解委托.事件的用途,理解它的C#实现方法,理解委托与事件为我们带来的好处. ...
- 接口包含内容概述2——委托与事件之惑
前言: 上篇博文<接口包含内容概述1--接口相关概述及接口中属性的实现>最后留有一疑问:一个接口为什么可以包含一个事件,却不能包含一个委托呢? 在这里要想对这个问题解答,首先必须搞清楚委托 ...
- 3.0 面向对象 委托和事件 异常和错误
一.委托和事件 委托和事件这两个概念是完全配合的.委托仅仅是函数指针,那就是说,它能够引用函数,通过传递地址的机制完成.委托是一个类,当你对它实例化时,要提供一个引用函数,将其作为它构造函数的参数.事 ...
- jquery事件委托_jQuery事件委托
jquery事件委托 jQuery使JavaScript中的事件处理变得容易. 但是,定义的事件处理程序越多,使用的内存就越多,最终可能会降低性能并导致UI变慢. 本文探讨事件委托如何帮助防止这种情况 ...
- 委托与事件-信用卡还款(C#)
一.功能说明 信用卡还款是银行系统的重要业务,业务流程说明如下: 用户有信用卡和储蓄卡,储蓄卡有查询余额和取款功能,信用卡能够查看账单金额.查看还款日和查看余额三个功能: 请使用委托与事件实现下列功能 ...
- 一个委托和事件的例子
一个委托和事件的例子 在以下情况下,请使用委托: 当使用事件设计模式时. 当封装静态方法可取时. 当调用方不需要访问实现该方法的对象中的其他属性.方法或接口时. 需要方便的组合. 当类可能需要该方法的 ...
最新文章
- mysql_connect() 不支持 请检查 mysql 模块是否正确加载
- python中文读音ndarray-numpy中的ndarray方法和属性
- 硬核!一套基于SpringBoot + Vue 的开源物联网智能家居系统!
- 大学计算机网络实验2,河南工业大学计算机网络实验报告2
- 比特币区块链将分道扬镳、Libra苦难继续,2020区块链进入关键时期!
- matlab实现2dpsk调制与解调,(完整版)matlab设计2DPSK信号调制与解调
- 基于数据挖掘的商业银行客户关系管理系统应用研究
- 缓存应用(一)Ehcache使用介绍
- cad多段线画圆弧方向_cad弧形多线(cad多段线怎么画弧线)
- 手机应用程序的可用性研究数据的获取、过滤、分析
- HBuilderX、微信开发者工具、VScode之间运行微信公众号
- 刘德华五条抖音粉丝五千万,流量平台用什么“留量”?
- fonts.googleapis.com 谷歌字体库加载过慢解决方案
- 【SIMCOM A7670C】Android8.1 4G Dongle 移植笔记
- 狼人杀超详入门1攻略之角色介绍
- 男人帮经典台词_男人帮经典语录大全
- 四大组件之BroadcastReceiver(二)-使用权限和常用的系统广播
- 36管四轮电动车控制器代码, 原理图和Pcb,完整可用。
- 群晖如何添加第三方源
- 2017年4月高等教育国际金融全国统一命题考试