在前面lambda章节中稍微提了一下委托,今天这章就让我们来深究一下委托。
委托的本质是一种类,他是继承MulticastDelegate类的。
而声明委托的关键字的delegate,如:public delegate void NoReturnNoParaOutClass();
但是刚才也讲了委托一种类,那按照类的声明,我们应该可以这样声明一个委托。
public class NoReturnNoParaOutClass: System.MulticastDelegate { }
只不过由于这种类比较特殊,因为它由于框架限定,这种做法在.net框架中是不被允许的,所以我们只能通过delegate关键字来声明委托。
下面讲讲委托应该怎么用。
用委托分为三个步骤:
1)声明委托
2)委托实例化
3)委托的调用
public delegate void NoReturnNoPara();//1声明委托 NoReturnNoPara method = new NoReturnNoPara(this.DoNothing);//2 委托的实例化
method.Invoke();//3 委托的调用 private void DoNothing() { Console.WriteLine("This is DoNothing"); }
在前面的章节也讲过,实例化委托时必须保证委托的签名和方法的签名保持一致。
再说个例子来阐述委托的用法。
假设有一个这样的需求,有一个汽车工厂,生产一款汽车。按照一般的做法应该是这样的
public void BuildCar() { Console.WriteLine("造一个发动机"); Console.WriteLine("造一个外壳"); Console.WriteLine("造一个底盘"); Console.WriteLine("造四个轮子"); Console.WriteLine("组装成一辆车"); Console.WriteLine("造好了一辆车。。"); }
但是由于社会在进步,车子的性能也在提升,车子的发动机也需要跟新换代,在以前只有自然吸气,现在需要增加 涡轮增压和电动,按照一般的改法的话。
public void BuildCar(EngineType engineType) { if (engineType == EngineType.NaturalInspiration) { Console.WriteLine("造一个自然吸气发动机"); } else if (engineType == EngineType.Turbo) { Console.WriteLine("造一个涡轮增压发动机"); } else if (engineType == EngineType.Electric) { Console.WriteLine("造一个电池发动机"); } Console.WriteLine("造一个外壳"); Console.WriteLine("造一个底盘"); Console.WriteLine("造四个轮子"); Console.WriteLine("组装成一辆车"); Console.WriteLine("造好了一辆GTR。。"); } public enum EngineType { NaturalInspiration = 0, Turbo = 1, Electric = 2 }
这种做法通过上端程序调用时传递一个参数来判断生产哪一种发动机。但是这样做对工厂的要求太大,万一以后出现了太阳能的岂不是对工厂的整体生产线都要进行修改,这样做的成本太大,不切合实际,而且现在的工厂一般都是只做组装工作,生产的任务都是外包出去。那么我们程序应该怎么改呢,毕竟程序源自于生活。要符合实际情况才行。
这个时候就需要使用委托了
我们把生产发动机都给剥离出来,在生产厂家只需要组装就行。
public void BuildEngineNaturalInspiration() { Console.WriteLine("造一个自然吸气发动机"); } public void BuildEngineTurbo() { Console.WriteLine("造一个涡轮增压发动机"); } public void BuildEngineElectric() { Console.WriteLine("造一个电池发动机"); }
那后再类中声明一个委托,并修改BuildCar方法。
public void BuildCar(BuildEngineDelegate method) { method.Invoke(); Console.WriteLine("造一个外壳"); Console.WriteLine("造一个底盘"); Console.WriteLine("造四个轮子"); Console.WriteLine("组装成一辆车"); Console.WriteLine("造好了一辆GTR。。"); } public delegate void BuildEngineDelegate();
在上端调用时直接传递一个方法,内部调用一下完成组装,这样做生产厂家就不必考虑应该怎么做了,你调用的时候直接给我,而我这里就完成一个组装工作。
CarFactory.BuildEngineDelegate method = new CarFactory.BuildEngineDelegate carFactory.BuildEngineNaturalInspiration); carFactory.BuildCar(method);
这样做厂家就对发动机怎么做的依赖性不那么强了。
再来讲个黑科技,有许多人写程序时不愿意写异常处理。在这里可以直接写一个异步处理,通过委托可以绑定在任何方法上。
/// <summary> /// 通用的异常处理 /// </summary> /// <param name="act">对应任何的逻辑</param> public static void SafeInvoke(Action act) { try { act.Invoke(); } catch (Exception ex)//按异常类型区分处理 { Console.WriteLine(ex.Message); } }
调用时只需要这样做就可以一步完成异步处理功能。
CarFactory.SafeInvoke(() => carFactory.BuildCar(method));
最后在讲讲多播委托,提到多播委托就不得不提提设计模式中的观察者模式。
我们假设有一只猫叫了一声,然后狗叫,老鼠跑,小孩就哭了,妈妈也醒了。爸爸就大叫,邻居也被吵醒,小偷也赶快溜了。
就因为猫的叫,导致一系列的触发动作
public class Cat { public void Miao() { Console.WriteLine("{0} Miao", this.GetType().Name); new Dog().Wang(); new Mouse().Run(); new Baby().Cry(); new Mother().Wispher(); new Brother().Turn(); new Father().Roar(); new Neighbor().Awake(); new Stealer().Hide(); } }
为了篇幅,其他类就不写上来了,都是简单的一些方法。。
假如猫叫一声,不是狗先叫,而是老鼠先跑,那岂不又要修改猫这个类,再说老鼠跑不跑管猫什么事,我叫我叫的,你爱干啥干啥,管我什么事,
但是这样的程序设计就必须修改猫这个类的方法。这个时候我们就要思考了,可不可把方法抽离出来,别那么依赖猫。其实用委托就可以实现这个功能。
声明一个委托,并在猫类中调用委托。
public Action MiaoAction;
public event Action MiaoActionEvent;
public void MiaoEvent()
{
Console.WriteLine("{0} MiaoActionEvent", this.GetType().Name);
if (MiaoActionEvent != null)
MiaoActionEvent.Invoke();
}
这个上端调用只需要这样做。
Console.WriteLine("************cat.MiaoEvent();***********"); cat.MiaoActionEvent += new Dog().Wang;//订阅 cat.MiaoActionEvent += new Mouse().Run; cat.MiaoActionEvent += new Baby().Cry; cat.MiaoActionEvent += new Mother().Wispher; cat.MiaoActionEvent += new Brother().Turn; cat.MiaoActionEvent += new Father().Roar; cat.MiaoActionEvent += new Neighbor().Awake; cat.MiaoActionEvent += new Stealer().Hide; cat.MiaoEvent();
在上面中提到了Event关键字,它就是事件,那么委托和事件是怎样的关系呢。
委托是一种类型,而事件是委托的一个实例
其实事件控制了实例的使用权限,更加安全。
事件不能再其他类中调用,只能在声明事件的类中调用,这就保证了事件的安全。
在观察者模式中。分为发布者,订户,和订阅者。
在本例中,猫就是那个发布者,而狗、小孩就是订户,最后调用时,上端是订阅者。而事件的发布只能声明在引起这一系列动作类中。
那么使用委托的意义是什么呢,使用委托主要是为了解耦,是程序之间的依赖关系不是那么强。还有多线程,这个以后再讲。