委托
1.什么是委托?
个人理解:委托就是把一个方法当做参数传入另一个方法中进行调用。 (委托的实质就是一个类,可以通过中间语言IL编译工具去查看源码)
2.委托的使用?
使用委托三部曲:(1)、委托的声明 。 (2)、委托的实例化。 (3)调用。
3.委托的种类?
(1).delegate (2).泛型委托 Func/ Action (3). event 和观察者模式
通过delegate 关键字来声明委托 举例:
1 public delegate void NoReturnNoPara(); // 无参数、无返回值的委托声明 2 public delegate void NoReturnWithPara(int x, int y);//1 声明委托 3 public delegate int WithReturnNoPara(); //无参数、int类型返回值委托声明 4 public delegate MyDelegate WithReturnWithPara(out int x, ref int y);//自定义委托类
1 private void DoNothing() //无参数无返回值的方法 2 { 3 Console.WriteLine("This is DoNothing 。。。。"); 4 }
1 NoReturnNoPara method = new NoReturnNoPara(this.DoNothing);// this表示是当前类的方法,也可以把方法拆分到其他类中。这里是为了使用方便。 2 //2 委托的实例化 要求传递一个参数类型 返回值都跟委托一致的方法, 3 method.Invoke();//3委托实例的调用 参数和委托约束的一致
上述三段代码,第一段是委托类型的声明; 第二段代码是为实例化委托做准备,通过实例化委托将DoNothing方法作为参数传入到委托中;第三段代码是最后调用DoNothing方法,执行DoNothing中的业务逻辑。注意:委托声明、委托的实例化 要求传递一个参数类型 返回值都跟委托一致的方法,委托实例的调用 参数和委托约束的一致。
Action Func .NetFramework3.0出现的
Action 系统提供 0到16个泛型参数 不带返回值 委托
1 //Action action = new Action(this.DoNothing); 2 Action action0 = this.DoNothing;//是个语法糖,就是编译器帮我们添加上new Action 3 action0.Invoke();
在这里的action0.invoke()和上述 method.Invoke() 执行的结果是一致的。这里推荐使用泛型委托。
Func 委托: 无参数int类型返回值
1 public int Get() 2 { 3 return 1; 4 } 5 6 //Func 系统提供 0到16个泛型参数 带泛型返回值 委托 7 Func<int> func0 = this.Get; 8 int iResult = func0.Invoke();
带参数有返回值的Func委托 int类型参数,字符串类型的返回值
1 private string ToString(int i) 2 { 3 return i.ToString(); 4 } 5 Func<int, string> func1 = this.ToString; //第一个是参数类型 ,第二个是返回值类型 6 func1.Invoke(0);
多播委托
多播委托有啥用呢?一个委托实例包含多个方法,可以通过+=/-=去增加/移除方法,Invoke时可以按顺序执行全部动作。
多播委托:任何一个委托都是多播委托类型的子类,可以通过+=去添加方法,+= 给委托的实例添加方法,会形成方法链,Invoke时,会按顺序执行系列方法。
1 Action method = this.DoNothing; 2 method += this.DoNothing; 3 method += DoNothingStatic; 4 method += new Student().Study; 5 method += Student.StudyAdvanced; 6 //method.BeginInvoke(null, null);//启动线程来完成计算 会报错,多播委托实例不能异步 7 foreach (Action item in method.GetInvocationList()) 8 { 9 item.Invoke(); 10 //item.BeginInvoke(null, null); 11 }
-= 给委托的实例移除方法,从方法链的尾部开始匹配,遇到第一个完全吻合的,移除,且只移除一个,如果没有匹配,就啥事儿不发生。
1 method -= this.DoNothing; 2 method -= DoNothingStatic; 3 method -= new Student().Study;//去不掉 原因是不同的实例的相同方法,并不吻合 4 method -= Student.StudyAdvanced; 5 method.Invoke(); 6 //中间出现未捕获的异常,直接方法链结束了
Func 的多播委托
1 { 2 Func<int> func = this.Get; 3 func += this.Get2; 4 func += this.Get3; 5 int iResult = func.Invoke(); 6 //结果是3 以最后一个为准,前面的丢失了。。所以一般多播委托用的是不带返回值的 7 }
解决泛型委托Func多返回值问题
举例:
1 public class DelegateMethod 2 { 3 //委托声明 4 public delegate string AddDelegate<T>(T i,T j); 5 6 /// <summary> 7 /// 8 /// </summary> 9 /// <returns></returns> 10 public string MyAdd(int k ,int j) 11 { 12 13 return k + j + " "; 14 } 15 16 public string MyFuncAdd(int k,int j) 17 { 18 19 return k + j + " "; 20 21 } 22 }
1 { 2 DelegateMethod Method = new DelegateMethod(); 3 List<string> list_str = new List<string>(); 4 5 Func<int, int, string> func = Method.MyFuncAdd; 6 func += Method.MyFuncAdd;
// GetInvocationList() //调用多播委托的方法列表 7 foreach (Func<int, int, string> item in func.GetInvocationList()) 8 { 9 list_str.Add(item.Invoke(1, 3)); 10 } 11 int i = 0; 12 foreach (var item in list_str) 13 { 14 i++; 15 Console.WriteLine($"第{i} 个值是:{ item}"); 16 } 17 }
event和观察者模式
事件可以理解成一个特殊的委托。
事件是无处不在的,winform无处不在---WPF---webform服务端控件/请求级事件
为啥要用事件?事件究竟能干什么?
事件(观察者模式)能把固定动作和可变动作分开,完成固定动作,把可变动作分离出去,由外部控制
搭建框架时,恰好就需要这个特点,可以通过事件去分离可变动作,支持扩展
控件事件:
启动Form---初始化控件Button---Click事件---+=一个动作
点击按钮--鼠标操作--操作系统收到信号--发送给程序--程序得接受信号,判断控件--登陆--
(事件只能类的内部发生)Button类自己调用Click--肯定是触发了Click事件---登陆动作就会执行
点击按钮--鼠标操作--操作系统收到信号--发送给程序--程序得接受信号,判断控件--支付--
(事件只能类的内部发生)Button类自己调用Click--肯定是触发了Click事件---支付动作就会执行
2次按钮操作,大部分东西都是一样的,就是具体业务不一样的,
封装的控件就完成了固定动作--接受信号&默认动作。。。
可变部分,就是事件---是一个开放的扩展接口,想扩展什么就添加什么
event限制权限,避免外部乱来。
举例: 一只猫,miao一声, 导致一系列的触发动作。(触发的动作有 狗吠,baby哭,母亲安慰baby......)
1 public class Dog : IObject 2 { 3 //public Dog(int id) 4 //{ } 5 6 public void DoAction() 7 { 8 this.Wang(); 9 } 10 public void Wang() 11 { 12 Console.WriteLine("{0} Wang", this.GetType().Name); 13 } 14 }
1 public class Baby : IObject 2 { 3 public void DoAction() 4 { 5 this.Cry(); 6 } 7 8 public void Cry() 9 { 10 Console.WriteLine("{0} Cry", this.GetType().Name); 11 } 12 }
1 public class Mother : IObject 2 { 3 public void DoAction() 4 { 5 this.Wispher(); 6 } 7 public void Wispher() 8 { 9 Console.WriteLine("{0} Wispher", this.GetType().Name); 10 } 11 }
下面通过猫类调用执行一系列动作。
1 public class Cat 2 { 3 public void Miao() 4 { 5 Console.WriteLine("{0} Miao", this.GetType().Name); 6 new Dog().Wang(); 7 new Baby().Cry(); 8 new Mother().Wispher(); 9 10 } 11 }
通过上述猫类调用执行可以出后续的一系列动作对猫类过度依赖, 依赖太重,依赖了多个类型,任何类型的变化都得修改猫。职责耦合,猫不仅自己Miao 还得找各种对象执行各种动作甚至控制顺序, 任意环节增加减少调整顺序 都得修改猫,实际上不该如此,猫就是猫,只做自己的事儿,需求是猫Miao一声---触发一系列的动作---代码还指定了动作, 猫只miao一声---触发一系列动作,动作从哪里来?不管,我只负责调用。
下面通过Action实现多播委托
先声明一个action委托 然后Invoke调用
1 public Action CatMiaoAction;
1 cat.CatMiaoAction += new Dog().Wang; 2 3 cat.CatMiaoAction += new Baby().Cry; 4 cat.CatMiaoAction += new Mother().Wispher;
cat.CatMiaoAction.Invoke(); //调用
使用Action委托调用后,cat类就是稳定的 不用做修改。可以任意扩展其他对象(不如还有一个哥哥类 执行其他的动作,只需要添加一个新的类,然后通过+=的形式将方法绑定上去就可以了),同时每个不同的类还可以自由调整执行的顺序。
我们来继续改造上述方法 通过事件来实现委托。
举例:
事件event:一个委托的实例,带一个event关键字,限制权限,只允许在事件声明类里面去invoke和赋值,不允许外面,甚至子类
1 public event Action CatMiaoActionHandler;
1 public void MiaoEvent() 2 { 3 Console.WriteLine("{0} MiaoEvent", this.GetType().Name); 4 this.CatMiaoActionHandler?.Invoke(); 5 6 }
1 cat.CatMiaoActionHandler += new Dog().Wang; 2 cat.CatMiaoActionHandler += new Mouse().Run; 3 cat.CatMiaoActionHandler += new Baby().Cry; 4 5 cat.CatMiaoActionHandler += new Mother().Wispher; 6 cat.CatMiaoActionHandler += new Brother().Turn; 7 cat.CatMiaoActionHandler += new Father().Roar; 8 cat.CatMiaoActionHandler += new Neighbor().Awake; 9 cat.CatMiaoActionHandler += new Stealer().Hide; 10 11 // cat.CatMiaoActionHandler.Invoke();
// 因为事件的权限限制 只能在事件声明的当前类Invoke调用 不能通过在其他类直接Invoke 所以封装了一个 MiaoEvent()方法在提供外部类使用
12 cat.MiaoEvent();
事件和委托的区别与联系?
委托是一种类型,事件是委托类型的一个实例,加上了event的权限控制