【C#系列】浅谈委托和事件
本篇文章更适合具有一定开发经验,一定功底,且对底层代码有所研究的朋友!!!
本篇文章主要采用理论和代码实例相结合方式来论述委托和事件,涉及到一些边界技术,如软件架构的OCP原则(开-闭原则),
软件架构解耦,设计模式(Sender-Order)和事件驱动模型,有一定难度和深度,不适合初级者。
第一部份 委托
关于委托内容,主要围绕下图来论述。
一 委托是什么(what)
(一)委托产生的背景之一
1.我们先来假设这样一个情景需求:
设计一个系统,使其满足如下条件:
(1)当前,只有中国人和英国人使用该系统;
(2)向系统输入用户名和相应的语言,将产生相应语言的问候语;
(3)后期,可能会有其他国家语言加入该系统(系统变化的部分) ;
2.技术方案实现
关于技术方案实现,我们可以采用下图中的三种方式之一。
为了更好地叙述委托,我们分别实现三种技术方案,并找出它们的关系。
2.1 一般实现
Code(控制台程序)
1 using System; 2 3 namespace DelegateDemo 4 { 5 class Program 6 { 7 static void Main(string[] args) 8 { 9 Console.WriteLine(GetGreetingContens("小王", "Chinese")); 10 Console.WriteLine(GetGreetingContens("Alan_beijing", "English")); 11 Console.WriteLine(GetGreetingContens("Linda", "Russian")); 12 Console.Read(); 13 } 14 15 //根据用户名和语言,获取问候语 16 public static string GetGreetingContens(string UserName, string Language) 17 { 18 //New 一个GreetToUsers对象 19 GreetToUsers greetToUsers = new GreetToUsers(); 20 //当然,你也可以使用switch开发语句来代替如下的if......else...... 21 if (Language == "Chinese") 22 { 23 return greetToUsers.ChinesePeople(UserName); 24 } 25 else if (Language == "English") 26 { 27 return greetToUsers.EnglishPeople(UserName); 28 } 29 else 30 { 31 return "抱歉,当前系统只支持汉语与英语(Sorry, the current system only supports Chinese and English.)"; 32 } 33 } 34 } 35 36 37 38 //定义基本问候类和方法 39 public class GreetToUsers 40 { 41 //Chinese People 42 public string ChinesePeople(string UserName) 43 { 44 string GreetContents = "您好!" + UserName; 45 return GreetContents; 46 } 47 48 //English People 49 public string EnglishPeople(string UserName) 50 { 51 string GreetContents = "Hello," + UserName + "!"; 52 return GreetContents; 53 } 54 } 55 56 }
Result
分析
2.2用接口实现
如上,我们分析了方案一中的问题,为了更好地解决方案一存在的问题,我们采用面向接口编程的形式来实现。
2.2.1 什么是面向接口编程?
面向接口编程,主要是解决软件架构设计中“动静问题”,即封装不变(静),剥离变化(抽出变化)。
Code:
1 using System; 2 3 namespace DelegateDemo 4 { 5 class Program 6 { 7 static void Main(string[] args) 8 { 9 GreetToChineseUsers greetToChinesUsers = new GreetToChineseUsers(); 10 GreetToEnglishUsers greetToEnglishUsers = new GreetToEnglishUsers(); 11 GreetToOtherUsers greetToOtherUsers = new GreetToOtherUsers(); 12 //Chinse Users 13 IGreetToUsers iGreetToChineseUsers = greetToChinesUsers; 14 Console.WriteLine(iGreetToChineseUsers.CountryPeople("小王", "Chinese")); 15 //English Users 16 IGreetToUsers iGreetToEnglishUsers = greetToEnglishUsers; 17 Console.WriteLine(iGreetToEnglishUsers.CountryPeople("Alan_beijing", "English")); 18 //Other Users 19 IGreetToUsers iGreetToOtherUsers = greetToOtherUsers; 20 Console.WriteLine(iGreetToOtherUsers.CountryPeople("Linda", "Russian")); 21 22 Console.Read(); 23 } 24 25 26 } 27 28 //系统输出问候语(变化的部分,语言为变化因子) 29 public interface IGreetToUsers 30 { 31 string CountryPeople(string UserName,string Language); 32 } 33 34 35 //汉语用户类 36 public class GreetToChineseUsers:IGreetToUsers 37 { 38 //Chinese People 39 public string CountryPeople(string UserName, string Language) 40 { 41 string GreetContents = "您好!" + UserName; 42 return GreetContents; 43 } 44 } 45 46 //英语用户类 47 public class GreetToEnglishUsers : IGreetToUsers 48 { 49 //English People 50 public string CountryPeople(string UserName, string Language) 51 { 52 string GreetContents = "Hello," + UserName + "!"; 53 return GreetContents; 54 } 55 } 56 57 //其他用户类 58 public class GreetToOtherUsers : IGreetToUsers 59 { 60 //English People 61 public string CountryPeople(string UserName, string Language) 62 { 63 return "Sorrry,当前系统只支持汉语与英语"; 64 } 65 } 66 67 }
result
分析:
(1)如上,我们将变化因子"语言"剥离出来,形成接口,以后只要每增加一个语言,只需实现接口即可,满足了OCP原则,基本解决了方案一中存在的问题;
(2)如上代码只是为了演示面向接口编程这个功能,并不完善,感兴趣的读者,可自行完善(如将语言定义为枚举类型等);
方案二中的代码,细心的读者会发现,Main方法中new了三个对象,假若以后系统有300门语言,那岂不New 300个类,这样的话,也不利于代码维护呀,怎么解决这个问题呢?(提示一下,采用设计模式抽象工厂即可解决该问题)
2.3 用委托实现
在这里,没接触过委托的读者,先跳过这部分,往下读,看完(三)怎样使用委托(How to use)后,再来看本部分。
Code
1 using System; 2 3 namespace DelegateDemo 4 { 5 class Program 6 { 7 static void Main(string[] args) 8 { 9 //根据语言判断,传递哪个方法的参数 10 Console.WriteLine("------------请输入用户名------------"); 11 string UserName = Console.ReadLine(); 12 Console.WriteLine("------------请输入语言------------"); 13 string Language = Console.ReadLine(); 14 Console.WriteLine("------------输出结果------------"); 15 GreetToUsers greetToUsers = new GreetToUsers(); 16 if (Language == "Chinese") 17 { 18 Console.WriteLine(GetGreetingContents(UserName, greetToUsers.ChinesePeople)); 19 } 20 else if (Language == "English") 21 { 22 Console.WriteLine(GetGreetingContents(UserName, greetToUsers.EnglishPeople)); 23 } 24 else 25 { 26 Console.WriteLine(GetGreetingContents(UserName, greetToUsers.OtherPeople)); 27 } 28 Console.Read(); 29 } 30 31 32 33 public static string GetGreetingContents(string UserName,DelegateGetGreeting delegateGetGreeting) 34 { 35 return delegateGetGreeting(UserName); 36 } 37 } 38 39 //定义委托 40 public delegate string DelegateGetGreeting(string UserName); 41 42 43 //定义基本问候类和方法 44 public class GreetToUsers 45 { 46 //Chinese People 47 public string ChinesePeople(string UserName) 48 { 49 string GreetContents = "您好!" + UserName; 50 return GreetContents; 51 } 52 53 //English People 54 public string EnglishPeople(string UserName) 55 { 56 string GreetContents = "Hello," + UserName + "!"; 57 return GreetContents; 58 } 59 //非英非汉 60 public string OtherPeople(string UserName) 61 { 62 return "Sorrry,当前系统只支持汉语与英语"; 63 } 64 } 65 66 }
Result
2.3 分析上述三种实现方案的关系
通过上诉三种方式的比较,我们容易得出委托的如下结论:
(1)抽象方法,屏蔽方法细节,调用只需传递方法名字即可;
(2)能够实现程序的解耦,松耦合(在方案一中,GetGreetingContens方法体内new了GreetToUsers对象,强耦合)
(3)委托一般扮演中间者的角色,这功能在委托事件中体现非常明显(第二部分 事件 将详细论述)
如我们在租房子时,可以直接找房东(技术实现的一般方法,强耦合,让租房者和房东直接联系),也可找中介(技术实现的委托,松耦合,租房者通过中介来与房东联系)
2.4 委托背景概述
委托的重要产生背景,就是事件驱动模型(关于什么是事件和事件驱动,在本文第二部份 事件 论述)。
(二) 委托定义
用delegate关键字定义委托(注意,委托是没有方法体的,类似接口里面的方法),在定义委托前,必须明确两个问题:
1.委托将要绑定的方法;
2.委托的形参类型,形参个数和委托的返回值必须与将要绑定的方法的形参类型,形参个数和返回值一致;
(三)相关概念
委托涉及的相关概念有函数指针,类型安全性、事件、Lambda表达式等
1.函数指针:在C++中,指针的一个类别,主要指向函数(变量指针,主要指向变量地址),可以把C#中的委托理解为函数指针;
2.类型安全性:在C++中,我们都知道指针是类型不安全的(返回值,返回类型和什么时候返回,这些都是未知的),而委托是类型安全的;
3.事件:可以把事件理解为委托的一种特例(在本文第二部份 事件 论述)
4.Lambda表达式:委托与Lambd表达式相结合,实现高效编程,与Jquery的“较少代码做更多的事”类似,委托与Lambda,Linq相结合,使较短代码就能实现比较复杂的功能(在本篇文章中不讲解Lambda与Lambda树,将在后续文章中讲解)
(四)委托组成
大致分为两部分:声明委托和注册方法(也叫绑定方法)
1.声明委托
用delegate声明;
2.绑定方法
绑定具体方法,传递方法名称;
(五) 委托种类
委托种类,一般分为多播委托和单播委托
1.单播委托:绑定单个方法
2.绑定多个方法
(六) 委托操作
1.绑定方法
2.解绑方法
二 委托能解决什么问题(Can do)
1.避免核心方法中存在大量的if....else....语句(或swich开关语句);
2.满足程序设计的OCP原则;
3.使程序具有扩展性;
4.绑定事件;
5.结合Lambda表达式,简化代码,高效编程;
6.实现程序的松耦合(解耦),这个在事件(event)中体现比较明显;
三 怎么使用委托(How to use)(本篇文章不谈匿名委托,匿名委托具体内容,将在Lambda章节讲解)
(一)委托的基本构成
通常地,使用委托的步骤与使用类的步骤是一样的。大致分为两步:定义委托和绑定方法(传递方法)
1.定义委托
用delegate关键字定义委托(注意,委托是没有方法体的,类似接口里面的方法),在定义委托前,必须明确两个问题:
(1).委托将要绑定的方法;
(2).委托的形参类型,形参个数和委托的返回值必须与将要绑定的方法的形参类型,形参个数和返回值一致;
public delegate 委托返回类型 委托名(形参)
例子:如上我们委托将要表示系统输出的问候语
a.委托将要绑定的方法
public string ChinesePeople(string UserName) { string GreetContents = "您好!" + UserName; return GreetContents; } //English People public string EnglishPeople(string UserName) { string GreetContents = "Hello," + UserName + "!"; return GreetContents; } //非英非汉 public string OtherPeople(string UserName) { return "Sorrry,当前系统只支持汉语与英语"; }
b.由如上方法可看出,方法的返回类型为string,方法有一个string类型的形参,在定义委托时,与其保持一致即可
//定义委托 public delegate string DelegateGetGreeting(string UserName);
2.绑定方法
使用委托时,将方法名字作为参数传递给委托即可
1 GreetToUsers greetToUsers = new GreetToUsers(); 2 GetGreetingContents(UserName, greetToUsers.ChinesePeople)
(二)委托绑定方法
1.绑定单个方法
绑定单个方法,将单个方法名字传给委托即可
1 static void Main(string[] args) 2 { 3 //根据语言判断,传递哪个方法的参数 4 Console.WriteLine("------------请输入用户名------------"); 5 string UserName = Console.ReadLine(); 6 Console.WriteLine("------------请输入语言------------"); 7 string Language = Console.ReadLine(); 8 Console.WriteLine("------------输出结果------------"); 9 GreetToUsers greetToUsers = new GreetToUsers(); 10 DelegateGetGreeting DGG; 11 if (Language == "Chinese") 12 { 13 //绑定单个方法 14 DGG = greetToUsers.ChinesePeople; 15 Console.WriteLine(GetGreetingContents(UserName, DGG)); 16 } 17 else if (Language == "English") 18 { 19 DGG = greetToUsers.EnglishPeople; 20 Console.WriteLine(GetGreetingContents(UserName, DGG)); 21 } 22 else 23 { 24 DGG = greetToUsers.OtherPeople; 25 Console.WriteLine(GetGreetingContents(UserName, DGG)); 26 } 27 28 Console.Read(); 29 }
另一种不太规范写法:不用GetGreetingContents(string UserName,DelegateGetGreeting delegateGetGreeting)方法
1 static void Main(string[] args) 2 { 3 //根据语言判断,传递哪个方法的参数 4 Console.WriteLine("------------请输入用户名------------"); 5 string UserName = Console.ReadLine(); 6 Console.WriteLine("------------请输入语言------------"); 7 string Language = Console.ReadLine(); 8 Console.WriteLine("------------输出结果------------"); 9 GreetToUsers greetToUsers = new GreetToUsers(); 10 DelegateGetGreeting DGG; 11 if (Language == "Chinese") 12 { 13 //绑定单个方法 14 DGG = greetToUsers.ChinesePeople; 15 Console.WriteLine(DGG(UserName)); 16 } 17 else if (Language == "English") 18 { 19 DGG = greetToUsers.EnglishPeople; 20 Console.WriteLine(DGG(UserName)); 21 } 22 else 23 { 24 DGG = greetToUsers.OtherPeople; 25 Console.WriteLine(DGG(UserName)); 26 } 27 28 Console.Read(); 29 }
之所以不规范,主要是在项目中,不利于代码的模块化。
2.绑定多个方法(多播委托)
注意:绑定多个方法时,委托范围类型必须为void类型,否则只返回最后一个绑定的值。
绑定多个方法,采用 += 绑定
1 using System; 2 3 namespace DelegateDemo 4 { 5 class Program 6 { 7 static void Main(string[] args) 8 { 9 10 GreetToUsers greetToUsers = new GreetToUsers(); 11 DelegateGetGreeting DGG; 12 13 //绑定多个方法 14 DGG = greetToUsers.ChinesePeople; 15 DGG += greetToUsers.EnglishPeople; 16 DGG("小王"); 17 18 Console.Read(); 19 } 20 21 22 } 23 24 //定义委托 25 public delegate void DelegateGetGreeting(string UserName); 26 27 28 //定义基本问候类和方法 29 public class GreetToUsers 30 { 31 //Chinese People 32 public void ChinesePeople(string UserName) 33 { 34 string GreetContents = "您好!" + UserName; 35 Console.WriteLine(GreetContents); 36 } 37 38 //English People 39 public void EnglishPeople(string UserName) 40 { 41 string GreetContents = "Hello," + UserName + "!"; 42 Console.WriteLine(GreetContents); 43 } 44 //非英非汉 45 public void OtherPeople(string UserName) 46 { 47 Console.WriteLine("Sorrry,当前系统只支持汉语与英语"); 48 } 49 } 50 51 } 52 53 54
3.解绑方法
解载绑定的方法,采用 -= 解绑
1 using System; 2 3 namespace DelegateDemo 4 { 5 class Program 6 { 7 static void Main(string[] args) 8 { 9 10 GreetToUsers greetToUsers = new GreetToUsers(); 11 DelegateGetGreeting DGG; 12 13 //绑定多个方法 14 DGG = greetToUsers.ChinesePeople; 15 DGG += greetToUsers.EnglishPeople; 16 17 //解绑ChinesePeople方法 18 DGG-= greetToUsers.ChinesePeople; 19 DGG("小王"); 20 21 Console.Read(); 22 } 23 24 25 } 26 27 //定义委托 28 public delegate void DelegateGetGreeting(string UserName); 29 30 31 //定义基本问候类和方法 32 public class GreetToUsers 33 { 34 //Chinese People 35 public void ChinesePeople(string UserName) 36 { 37 string GreetContents = "您好!" + UserName; 38 Console.WriteLine(GreetContents); 39 } 40 41 //English People 42 public void EnglishPeople(st
相关文章:
版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。
上一篇: 让别人舒服就是教养
发表评论