C# 委托事件
1. Action 和 Func
//Action Func .NetFramework3.0出现的 //Action 系统提供 0到16个泛型参数 不带返回值 委托 //Action action = new Action(this.DoNothing); Action action0 = this.DoNothing;//是个语法糖,就是编译器帮我们添加上new Action Action<int> action1 = this.ShowInt; Action<int, string, DateTime, long, int, string, DateTime, long, int, string, DateTime, long, int, string, DateTime, long> action16 = null; //Func 系统提供 0到16个泛型参数 带泛型返回值 委托 Func<int> func0 = this.Get; int iResult = func0.Invoke(); Func<int, string> func1 = this.ToString; Func<int, string, DateTime, long, int, string, DateTime, long, int, string, DateTime, long, int, string, DateTime, long, string> func16 = null;
Action action0 = this.DoNothing; NoReturnNoPara method = this.DoNothing; //为啥框架要提供这种委托呢? 框架提供这种封装,自然是希望大家都统一使用Action/Func this.DoAction(action0); //this.DoAction(method); //委托的本质是类,Action和NoReturnNoPara是不同的类,虽然实例化都可以传递相同的方法,但是没有父子关系,所以是不能替换的 //就像Student和Teacher两个类,实例化都是传递id/name,但是二者不能替换的 //更进一步,框架中出现过N多个委托,委托的签名是一致的,实例化完的对象缺不能通用 //new Thread(new ParameterizedThreadStart()).Start(); //ThreadPool.QueueUserWorkItem(new WaitCallback()) //Task.Run(new Action<object>()); //本身实例化的时候,参数都是一样的,都是id/name,但却是不同的类型,导致没法通用 //Action和Func 框架预定义的,新的API一律基于这些委托来封装 //因为.Net向前兼容,以前的版本去不掉了,保留着,这是历史包袱 //此后,大家就不要再定义新的委托了,包括大家的作业
//多种途径实例化 实例化的限制就是方法的参数列表&返回值类型必须和委托约束的一致 { Action method = this.DoNothing; } { Action method = DoNothingStatic; } { Action method = new Student().Study; } { Action method = Student.StudyAdvanced; }
2. 多播委托
//多播委托有啥用呢?一个委托实例包含多个方法,可以通过+=/-=去增加/移除方法,Invoke时可以按顺序执行全部动作 //多播委托:任何一个委托都是多播委托类型的子类,可以通过+=去添加方法 //+= 给委托的实例添加方法,会形成方法链,Invoke时,会按顺序执行系列方法 Action method = this.DoNothing; method += this.DoNothing; method += DoNothingStatic; method += new Student().Study; method += Student.StudyAdvanced; //method.BeginInvoke(null, null);//启动线程来完成计算 会报错,多播委托实例不能异步 foreach (Action item in method.GetInvocationList()) { item.Invoke(); item.BeginInvoke(null, null); } //method.Invoke(); //-= 给委托的实例移除方法,从方法链的尾部开始匹配,遇到第一个完全吻合的,移除,且只移除一个,如果没有匹配,就啥事儿不发生 method -= this.DoNothing; method -= DoNothingStatic; method -= new Student().Study;//去不掉 原因是不同的实例的相同方法,并不吻合 method -= Student.StudyAdvanced; method.Invoke(); //中间出现未捕获的异常,直接方法链结束了
Func<int> func = this.Get; func += this.Get2; func += this.Get3; int iResult = func.Invoke(); //结果是3 以最后一个为准,前面的丢失了。。所以一般多播委托用的是不带返回值的
3. 事件
/// 事件event真的是无处不在的, /// winform无处不在---WPF---webform服务端控件/请求级事件 /// /// 为啥要用事件?事件究竟能干什么? /// 事件(观察者模式)能把固定动作和可变动作分开,完成固定动作,把可变动作分离出去,由外部控制 /// 搭建框架时,恰好就需要这个特点,可以通过事件去分离可变动作,支持扩展 /// /// 控件事件: /// 启动Form---初始化控件Button---Click事件---+=一个动作 /// /// 点击按钮--鼠标操作--操作系统收到信号--发送给程序--程序得接受信号,判断控件--登陆-- /// (事件只能类的内部发生)Button类自己调用Click--肯定是触发了Click事件---登陆动作就会执行 /// /// 点击按钮--鼠标操作--操作系统收到信号--发送给程序--程序得接受信号,判断控件--支付-- /// (事件只能类的内部发生)Button类自己调用Click--肯定是触发了Click事件---支付动作就会执行 /// /// 2次按钮操作,大部分东西都是一样的,就是具体业务不一样的, /// 封装的控件就完成了固定动作--接受信号&默认动作。。。 /// 可变部分,就是事件---是一个开放的扩展接口,想扩展什么就添加什么 /// event限制权限,避免外部乱来,
//事件event:一个委托的实例,带一个event关键字 //限制权限,只允许在事件声明类里面去invoke和赋值,不允许外面,甚至子类 //事件和委托的区别与联系? //委托是一种类型,事件是委托类型的一个实例,加上了event的权限控制 //Student是一种类型,Tony就是Student类型的一个实例, public event Action CatMiaoActionHandler;
Console.WriteLine("&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&"); cat.CatMiaoActionHandler += new Dog().Wang; cat.CatMiaoActionHandler += new Mouse().Run; cat.CatMiaoActionHandler += new Baby().Cry; //cat.CatMiaoActionHandler.Invoke(); //cat.CatMiaoActionHandler = null; cat.CatMiaoActionHandler += new Mother().Wispher; cat.CatMiaoActionHandler += new Brother().Turn; cat.CatMiaoActionHandler += new Father().Roar; cat.CatMiaoActionHandler += new Neighbor().Awake; cat.CatMiaoActionHandler += new Stealer().Hide; cat.MiaoEvent();
public void MiaoEvent() { Console.WriteLine("{0} MiaoEvent", this.GetType().Name); this.CatMiaoActionHandler?.Invoke(); Console.WriteLine("*&^&*^*^*(^&*^&*^&*^&*^"); Console.WriteLine("*&^&*^*^*(^&*^&*^&*^&*^"); Console.WriteLine("*&^&*^*^*(^&*^&*^&*^&*^"); Console.WriteLine("*&^&*^*^*(^&*^&*^&*^&*^"); Console.WriteLine("*&^&*^*^*(^&*^&*^&*^&*^"); }
4. 观察者模式
例:
老大带着小弟去谈判,如果老大举起左手则小A行动,如果老大举起右手则小B行动,如果情况不妙老大摔了杯子则小A小B一起行动。
传统编码:
public class Boss { private MemberA memberA = new MemberA(); private MemberB memberB = new MemberB(); /// <summary> /// 举手 /// </summary> /// <param name="hand"></param> public void Raise(string hand) { if (hand == "左") { memberA.Attack(); } else if (hand == "右") { memberB.Attack(); } } /// <summary> /// 摔杯子 /// </summary> public void Fail() { memberA.Attack(); memberB.Attack(); } } public class MemberA { public void Attack() { //do something... } } public class MemberB { public void Attack() { //do something... } }
好了,我们按照这种方式完成了编码,此时需求变更了,对方人数过多我们需要多带些人手,增加MemberC、MemberD,就要在Boss类中频繁的修改Raise()和Fail(),使程序毫无扩展性,那么我们改为事件委托的方式试试看。
#region 观察者模式 /// <summary> /// 左/右手 /// </summary> public enum Hand { Left, Right } /// <summary> /// 监视对象 /// </summary> public class Boss { //我要举起的手 public Hand MyHand { get; set; } //举手-委托-事件 public delegate void RaiseDelegate(Hand myHand); public event RaiseDelegate RaiseEventHandler; public event Action<Hand> RasieEventHandeler1; //上面的另一种写法 //摔杯子-委托-事件 public delegate void FailDelegate(); public event FailDelegate FailEventHandler; public event Action FailEventHandler1; //上面的另一种写法 public void RaiseHand() { Console.WriteLine("举手-" + MyHand); if (RaiseEventHandler != null) { RaiseEventHandler(MyHand); } if (RasieEventHandeler1 != null) { RasieEventHandeler1.Invoke(MyHand); } } public void FailAll() { Console.WriteLine("摔杯子"); if (FailEventHandler != null) { FailEventHandler(); } if (FailEventHandler1 != null) { FailEventHandler1(); } } } /// <summary> /// 监视者A /// </summary> public class MemberA { public void HandLeft(Hand myHand) { if (myHand == Hand.Left) Attack(); } public void Attack() { Console.WriteLine("MemberA攻击"); } } /// <summary> /// 监视者B /// </summary> public class MemberB { public void HandRight(Hand myHand) { if (myHand == Hand.Right) Attack(); } public void Attack() { Console.WriteLine("MemberB攻击"); } }
#region 调用代码 Boss boss = new Boss(); MemberA memberA = new MemberA(); MemberB memberB = new MemberB(); boss.RaiseEventHandler += memberA.HandLeft; boss.FailEventHandler += memberA.Attack; boss.RaiseEventHandler += memberB.HandRight; boss.FailEventHandler += memberB.Attack; boss.MyHand = Hand.Left; boss.RaiseHand(); boss.FailAll(); #endregion
改造完成,又有需求变更了,增加老大带的人手,此时我们主类Boss类不需要进行任何修改,只需要增加新的小弟类MemberC、MemberD等等即可,这样也实现了“对扩展说Yes,对修改说No”的宗旨。
.Net中的事件与委托
仔细看会发现,我们写的和.Net中自带的委托事件不太一样,尤其是Webform时的那些按钮事件
首先.Net有几条编码规范:
1、委托类型的名称都应该以EventHandler结尾
2、委托的原型定义,要无返回值void,要有两个输入参数,一个object类型,一个EventArgs类型(或继承EventArgs)
3、事件的名称为委托去掉EventHandler剩余的部分
4、继承EventArgs的子类名称都应以EventArgs结尾
结合我们的例子解释一下:
1、委托声明原型参数中object对象代表了Subject,也就是监视对象,本例中就是Boss自己,注册委托的方法(MemberA)中可以访问触发事件的对象(Boss)
2、EventArgs对象代表了Observer,也就是监视者,其中包含了监视者所需要的数据,本例中就是举起的那只手左还是右
这样的设计并不是硬套编码规范,它还具有很强的灵活性,如果需要在Observer下显示Subject的信息,比如想显示出手(MemberA)下保护了那个老大(Boss.Name),就需要在委托方法中增加很多参数,而这样我们直接
把Boss对象引用传递给MemberA的方法,就可以根据自己的需求直接访问Boss了
#region 观察者模式 /// <summary> /// 左/右手 /// </summary> public enum Hand { Left, Right } /// <summary> /// 监视对象 /// </summary> public class Boss { //我的手 public Hand MyHand { get; set; } //我的名字 public string MyName { get { return "Taiyonghai"; } } //举手-委托-事件 public delegate void RaiseEventHandler(object sender, RaiseEventArgs e); public event RaiseEventHandler Raise; //举手事件参数对象(此对象就是为了整合所有监视者关心的属性,便于直接传递给监视者使用,也就是监视者需要什么字段就定义什么字段) public class RaiseEventArgs : EventArgs { public Hand myHand; public RaiseEventArgs(Hand myHand) { this.myHand = myHand; } } //举手后需要执行事件方法(执行是否调用已注册到此事件上的方法,设为虚方法为继承此类的子类提供的可重写的能力, //以便子类自己选择是否接受监视并调用已注册方法,或者不被监视不调用任何方法) public virtual void OnRaiseHand(RaiseEventArgs e) { if (Raise != null) //有否已有注册方法 { Raise(this, e); //调用所有已注册的方法 } } //举手方法 public void RaiseHand() { Console.WriteLine("举手-" + MyHand); RaiseEventArgs e = new RaiseEventArgs(MyHand); OnRaiseHand(e); } //摔杯子-委托-事件 public delegate void FailEventHandler(object sender); public event FailEventHandler Fail; //摔杯子需要执行事件方法 public virtual void OnFailAll() { if (Fail != null) { Fail(this); } } //摔杯子方法 public void FailAll() { Console.WriteLine("摔杯子"); OnFailAll(); } } /// <summary> /// 监视者A /// </summary> public class MemberA { public void HandLeft(object sender, Boss.RaiseEventArgs e) { Boss boss = (Boss)sender; if (e.myHand == Hand.Left) Console.WriteLine("MemberA单独行动,保护" + boss.MyName); } public void Attack(object sender) { Boss boss = (Boss)sender; Console.WriteLine("MemberA一起行动,保护" + boss.MyName); } } /// <summary> /// 监视者B /// </summary> public class MemberB { public void HandRight(object sender, Boss.RaiseEventArgs e) { Boss boss = (Boss)sender; if (e.myHand == Hand.Right) Console.WriteLine("MemberB单独行动,保护" + boss.MyName); } public void Attack(object sender) { Boss boss = (Boss)sender; Console.WriteLine("MemberB一起行动,保护" + boss.MyName); } } #endregion
#region 调用代码 Boss boss = new Boss(); MemberA memberA = new MemberA(); boss.Raise += memberA.HandLeft; //普通注册方法(也可以静态方法注册) boss.Fail += memberA.Attack; boss.Raise += new MemberB().HandRight; //匿名对象注册方法 boss.Fail += new MemberB().Attack; boss.MyHand = Hand.Left; boss.RaiseHand(); boss.FailAll(); #endregion
付费内容,请联系本人QQ:1002453261
本文来自博客园,作者:明志德道,转载请注明原文链接:https://www.cnblogs.com/for-easy-fast/p/12336234.html