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

 

posted @ 2020-02-20 16:21  明志德道  阅读(554)  评论(0编辑  收藏  举报