委托
一、什么是委托?
委托就是一种引用方法的类型。一旦为委托分配了方法,委托将与该方法具有完全相同的行为。委托方法的使用可以像其他任何方法一样,具有参数和返回值。委托可以看做是对函数的抽象,是函数的“类”,委托的实例将代表一个具体的函数。
一个委托可以搭载多个方法,所有方法被依次唤起。委托对象所搭载的方法并不需要属于同一个类。但委托也是有前提的,那就是委托对象所搭载的所有方法必须拥有相同的参数列表和返回值类型。
二、为什么要用委托
简单点来说,就是某一件事你不想做,不会做或者没有权限做,就可以找个人代替你做。
三、怎么使用委托?
委托的关键字是delegate,委托的定义和类的定义一样,所以凡是能定义类的地方也是可以定义委托的,public delegate void MyDelegate();这个定义了一个无返回值,无参的委托类型。
class Program { public delegate void MyDelegate(); static void Main(string[] args) { MyDelegate myMessage = new MyDelegate(MyMethod); myMessage(); Console.ReadLine(); } public static void MyMethod() { Console.WriteLine("我是通过委托调用的"); } }
(1)自定义委托
现在不是很喜欢搞多国语言化的吗?看看如何让我们的程序会说多种语言吧!
/// <summary> /// the English speaker. /// </summary> /// <param name="name">The name.</param> public void EnglishSpeaker(string name) { Console.WriteLine( string.Format("Hello my name is {0} and I am English speaker.\n", name)); } /// <summary> /// the Chineses speaker. /// </summary> public void ChineseSpeaker(string name) { Console.WriteLine( string.Format("您好我的名字叫{0},我是讲普通话的。\n", name)); }
现在我们有两个方法分别是说普通话和英语,现在我们的程序会说普通话和英语啦。现在我们考虑究竟什么时候讲普通话什么时候讲英语,那不简单我们加个判断就OK啦,是的我们可以通过switch或者if else就可以实现啦。
/// <summary> /// 根据上下文调用不同的方法 /// </summary> /// <param name="name">string</param> /// <param name="lang">enum</param> private static void Say(string name, Language lang) { switch (lang) { case Language.Chinese: Program.ChineseSpeaker(name); break; case Language.English: Program.EnglishSpeaker(name); break; default : break; } }
但假设我们现在又要增加新的语言西班牙语,同样我们可以增加西班牙语,但我们必须修改switch语句增加判断,这不符合OOP中的OCP(对扩展开放,对修改关闭原则),这时候委托该登场。
/// <summary> /// Define speak delegate. /// </summary> /// <param name="name"></param> private delegate void SpeakDelegate(string name);
首先我们定义了一种委托类型SpeakDelegate,然后我们通过修改Say方法看看该如何使用委托变量。
/// <summary> /// The base say function. /// </summary> /// <param name="name">The name.</param> /// <param name="speaker">The speaker.</param> private static void Say(string name, SpeakDelegate speaker) { ///Inoke the speaker function. speaker(name); }
现在我们的参数已经不是枚举类型了,而是一个委托类型变量,而且实现的具体代码也没有了switch语句了,比之前简单了许多。现在大家知道如何去调用Say方法吧!没错我们只需传递一个name和一个具体实现函数名就OK了。
///传递函数名进行委托方法绑定 Program.Say("lucky小柒", ChineseSpeaker); Program.Say("lucky小柒", EnglishSpeaker);
(2)事件委托
举一个简单的例子,.NET中经常使用的控件Button,当我们把Button 控件 drap and drop到界面,然后双击界面的Button我们发现程序中自动生成了一个响应Button的事件方法,然后我们给事件方法添加Code之后,当我们点击该Button就响应该方法了,但我们没有看到代码中有任何的委托和事件之类的定义,其实这些.NET都已经做好了。
事件对委托进行了封装,对外只提供两个方法:add,remove,通过添加和移除来绑定事件。
/// 使用事件对委托进行封装 /// </summary> public class Say { /// <summary> /// 封装委托字段 /// </summary> public static event SpeakDelegate speakDelegate; /// <summary> /// 调用委托具体实现方法 /// </summary> /// <param name="name"></param> public static void SayManager(string name) { speakDelegate(name); } } /// <summary> /// 客户端调用委托 /// </summary> /// <param name="args"></param> static void Main(string[] args) { Say.speakDelegate += Program.ChineseSpeaker; Say.speakDelegate += Program.EnglishSpeaker; Say.SayManager("Jackson"); Console.ReadKey(); }
事件委托实现观察者模式
故事
小雪是一个非常漂亮的女孩,漂亮的女孩总是有很多的追求者,而且追求者的队伍在不断的变动,随时有人进入这个队伍,也有人退出。男孩们追求女孩时总是表现出120%的关心,当小雪私自游玩时总是不断收到追求者询问小雪位置变动的消息,小雪也不胜其烦,但小雪是如此的一个善良的女孩,她总是打断自己正常的生活回复男孩们的消息。而男孩们由于要不断的关心小雪的位置变化也弄的精疲力竭,而且还影响正常的工作。
在这样一个简单的故事场景中我们发现了什么?来看看小雪和男孩们的烦恼:
男孩们必须不断的询问小雪的位置变化,从而打断正常的工作
小雪也要不断的接受男孩们的询问,有的时候小雪的位置并没有发生变化,还是要不断的回复男孩们的询问,也影响正常的工作。
如果给各个男孩们回复问题的方式都不尽相同,小雪还要知道不同的回复方式,而且不断的有新的男孩们增加进来,还不知道未来有什么新的回复方式。
看到这么多烦恼,我们给小雪和男孩们提出了解决方案:
一款带有GPRS功能的手机,该手机保存着一个订阅位置变化短信通知的电话列表,当该手机检测到位置发生变化就会向这个订阅列表里的所有手机发送短信。看到这个解决方案,男孩们和小雪都应该松一口气,他们各自都可以按照自己正常的生活习惯,只有状态发生变化时候各自才会进行通信。
sing System; //所有观察者都必须实现 public interface IBoy { //向男孩们显示小雪位置情况,也就是向观察者发送消息,观察者还可以对此做出反馈 void Show(string address); } //男孩A,一个观察者 public class BoyA : IBoy { public void Show(string address) { //假设经过处理后为韩文的地址 Console.WriteLine("A:" + address); } } //男孩B,又一个观察者 public class BoyB : IBoy { public void Show(string address) { //假设经过处理后为英语的地址 Console.WriteLine("B:" + address); } } //下面看看小雪的实现,也就是被观察者,主要看看那个订阅的电话列表和怎样将消息通知给观察者. using System; using System.Collections.Generic; public class GPRSMobile { //保存一个观察者列表 private List<IBoy> boys = null; private string address = ""; public GPRSMobile() { boys = new List<IBoy>(); } //添加观察者 public void AddBoy(IBoy b) { boys.Add(b); } public void RemoveBoy(IBoy b) { boys.Remove(b); } //通知 private void Notify(string address) { for (int i = 0; i < boys.Count; i++) { boys[i].Show(address); } } //位置发生变化 public void OnAddressChanaged(string newAddress) { //假设这里的地址是中文形式的 Notify(newAddress); } }
观察者模式的实现有这样几条规律:
第一,被观察者必须保存着一个观察者列表。
第二,所有的观察者必须实现一个统一的接口。
(3)多播委托
前面使用的每个委托都只包含一个方法调用。调用一个委托就调用一个方法调用。如果要通过一个委托调用多个方法,那就需要使用委托的多播特性。如果调用多播委托,就可以按委托添加次序连续调用多个方法。为此,委托的签名就必须返回void;否则,就只能得到委托调用的最后一个方法的结果,接下来看看多播实现。
namespace Multi_Delegate { delegate void StringProcessor(); public class Person { private string _Name; public Person(string name) { this._Name = name; } public void Say() { Console.WriteLine("Hello my name is {0}, what's your name.\n", this._Name); } public void Reply() { Console.WriteLine("Hello my name is {0} and nice to meet you.\n", this._Name); } } class Program { static void Main(string[] args) { Person Jack = new Person("Jack"); Person Oliver = new Person("Oliver"); StringProcessor sp = null; //绑定多播方法调用 sp += Jack.Say; sp += Oliver.Reply; sp(); Console.ReadKey(); } } }