介绍
首先委托是对方法进行封装的一个类,它将方法作为参数来调用。
下面来简单看看委托的三部曲,委托的声明,实例化,调用,代码如下:
1 class DelegeteTest 2 { 3 public delegate void DelegateShow(); //声明 4 public void Show() 5 { 6 { 7 //普通调用 8 DoSomething(); 9 } 10 { 11 DelegateShow delegateShow = new DelegateShow(DoSomething); //实例化,方法作为参数传递 12 delegateShow(); // 委托调用 13 } 14 //委托将方法包装成了一个对象 15 } 16 17 /// <summary> 18 /// 普通方法 19 /// </summary> 20 public void DoSomething() 21 { 22 Console.WriteLine("this is doSomething"); 23 } 24 }
这是一个简单的委托,那么为什么要这样使用委托呢?这里我引入一个简单的例子,请看下面的代码:
1 public class People 2 { 3 /// <summary> 4 /// 对某人的简单的问候 5 /// </summary> 6 public void SayHI(string Name) 7 { 8 Console.WriteLine("你好,{0}", Name); 9 } 10 } 11 12 13 { 14 People p = new People(); 15 p.SayHI("张三"); 16 }
这里新建一个People 类 实现一个简单的问候方法。随着程序的运行,我们的需求可能会发生变化(唉,需求变化乃万恶之源)。
这里我们扩展一下。我们的问候对象 要区分中国人,美国人,日本人,方法就要改对应国家的语言。一般我们对原有的方法作出以下修改。
public class People { /// <summary> /// 对多国家的人进行问候 /// </summary> public void SayHI(string Name,PeopleType peropleType) { if (peropleType == PeopleType.Chinese) { Console.WriteLine("你好,{0}", Name); } else if (peropleType == PeopleType.American) { Console.WriteLine("Hello,{0}", Name); } else if (peropleType == PeopleType.Japanese) { Console.WriteLine("&*%$%^%$^,{0}", Name); } } public enum PeopleType { Chinese=0, American=1, Japanese=2 } } //--------------------------------------------------------------------------------------- { People p = new People(); p.SayHI("张三", People.PeopleType.Chinese); p.SayHI("Tom", People.PeopleType.American); p.SayHI("漩涡鸣人", People.PeopleType.Japanese); }
这样做虽然实现了功能,但是这样做不推荐,因为每增加一个分支(比如又新加了法国人,韩国人等等),都需要去修改SayHI()方法和新增一个PeopleType。这样系统耦合性太大,SayHI方法包法了所有的问候逻辑。于是我们继续升级,代码如下:
/// <summary> /// 对中国人的问候 /// </summary> /// <param name="Name"></param> public void SayHIChinese(string name) { Console.WriteLine("你好,{0}", name); } /// <summary> /// 对美国人的问候 /// </summary> /// <param name="name"></param> public void SayHIAmerican(string name) { Console.WriteLine("Hello,{0}", name); } /// <summary> /// 对日本人的问候 /// </summary> /// <param name="name"></param> public void SayHIJapanese(string name) { Console.WriteLine("&*%$%^%$^,{0}", name); } Console.WriteLine("-----------升级---------"); { People p = new People(); p.SayHIChinese("张三丰"); p.SayHIAmerican("Jerry"); p.SayHIJapanese("旗木卡卡西"); }
这样我们把原来分支里面的拆分成上面的多个方法。这样就可以适应人物类型的扩展了,看似没有什么问题。这时我们的问题来了,我们的业务要进行扩展。我们需要在每次问候之前,结束都需要加入日志记录。代码如下:
1 /// <summary> 2 /// 对中国人的问候 3 /// </summary> 4 /// <param name="Name"></param> 5 public void SayHIChinese(string name) 6 { 7 Console.WriteLine("开始问候"); //增加日志 8 Console.WriteLine("你好,{0}", name); 9 Console.WriteLine("问候结束");//增加日志 10 } 11 12 /// <summary> 13 /// 对美国人的问候 14 /// </summary> 15 /// <param name="name"></param> 16 public void SayHIAmerican(string name) 17 { 18 Console.WriteLine("开始问候"); //增加日志 19 Console.WriteLine("Hello,{0}", name); 20 Console.WriteLine("问候结束");//增加日志 21 } 22 23 /// <summary> 24 /// 对日本人的问候 25 /// </summary> 26 /// <param name="name"></param> 27 public void SayHIJapanese(string name) 28 { 29 Console.WriteLine("开始问候"); //增加日志 30 Console.WriteLine("&*%$%^%$^,{0}", name); 31 Console.WriteLine("问候结束");//增加日志 32 }
这样做显然太麻烦,每个方法都要去添加公共逻辑(增加日志)。所以要有一个公有方法添加日志。那我们又回到最初的方法。
1 /// <summary> 2 /// 对多国家的人进行问候 3 /// </summary> 4 public void SayHI(string Name,PeopleType peropleType) 5 { 6 Console.WriteLine("开始问候"); //增加日志 7 if (peropleType == PeopleType.Chinese) 8 { 9 Console.WriteLine("你好,{0}", Name); 10 } 11 else if (peropleType == PeopleType.American) 12 { 13 Console.WriteLine("Hello,{0}", Name); 14 } 15 else if (peropleType == PeopleType.Japanese) 16 { 17 Console.WriteLine("&*%$%^%$^,{0}", Name); 18 } 19 Console.WriteLine("问候结束");//增加日志 20 }
以上方法可以增加公有逻辑,但是是存在分支。把方法分开写,不存在分支,但是每个方法里面要增加公有逻辑,那我们有什么办法把这两种合并在一起呢。有的,引入委托解决这一问题。代码如下:
/// <summary> /// 完美方法: /// 1,方便增加公有逻辑 /// 2,不因逻辑而变化 /// 直接传入逻辑(方法) /// </summary> public void SayHIPerfect(string name,SayHIDelegete sayHiDelegete) { Console.WriteLine("开始问候"); //增加日志 sayHiDelegete.Invoke(name); Console.WriteLine("问候结束");//增加日志 } /// <summary> /// 对中国人的问候 /// </summary> /// <param name="Name"></param> public void SayHIChinese(string name) { Console.WriteLine("你好,{0}", name); } /// <summary> /// 对美国人的问候 /// </summary> /// <param name="name"></param> public void SayHIAmerican(string name) { Console.WriteLine("Hello,{0}", name); } /// <summary> /// 对日本人的问候 /// </summary> /// <param name="name"></param> public void SayHIJapanese(string name) { Console.WriteLine("&*%$%^%$^,{0}", name); } Console.WriteLine("-----------完美版本---------"); { People p = new People(); { SayHIDelegete method = new SayHIDelegete(p.SayHIChinese); p.SayHIPerfect("林青霞",method); } { SayHIDelegete method = new SayHIDelegete(p.SayHIAmerican); p.SayHIPerfect("Lilei", method); } { SayHIDelegete method = new SayHIDelegete(p.SayHIJapanese); p.SayHIPerfect("山本一夫", method); } }
总结
委托的作用
1.解藕,将系统 里面的公共逻辑和业务逻辑分开。
2.除本文中讲到的解藕,委托还用于事件,观察者模式,lamada表达式,异步多线程等等,可以说委托在我们的编程中无处不在。