C# 通俗说 委托(和事件)

1.闲聊

  编码一两年,

  我走过了字段,

  我跑过了类,

  却翻不过方法。(不能灵活使用方法吧)

  (写这篇博客全程听将夜中《永夜》歌曲写完的,一气呵成,安利一下)

2.叙事

  我们在编码中,经常捣鼓来捣鼓去的无非就是 “ 字段,方法 ,类这三种。像字段,类的使用(引用)很简单,但是,方法的使用(引用,传递)貌似,有点“模糊”不清。甚至有些初学害怕委托,害怕见到delegate这个关键字。

但是一般稍微成熟点的大佬的方法都是充满delegate的,是不是,哈哈哈。因此,你不得不去使用。关于方法引用的好处嘛,一句话,逼格很高,你必须学会去使用,这是上升为中级程序员必须会的。(代码可读性,耦合性等好处大大有)

  那么,现在如果让我来设计如何传递(引用)方法,我是如何设计的。

3.如何设计传递(引用)方法

  1)方法大概是什么样

     大概就是可以有/无返回参数,有传入参数,传入参数数量不一定,类型不一定。

即如图

  

  2)明确方法定义

      因为如上图可知,方法类型是很不明确的,那么我们需要明确好方法。这里用TOM(type of method)关键字作为我们明确方法类型的关键字。 

     public TOM void tom1();//tom1是无返回值,无传入参数的方法类型
        public TOM void tom2(string str);//tom2是i无返回值,有一个string类型参数
        public TOM int tom3(int num, string str);//tom3是int型返回值,有一个int传入参数+一个string型传入参数

 我们用这样的规则(这个规则,是需要死记),就可以明确定义出任意我们 想要的具体方法类型。(这应该很容易理解的吧)

  3)使用方法类型

   由上述2可知,有三种例子类型。很简单

      public TOM void tom1();//tom1是无返回值,无传入参数的方法类型
        public TOM void tom2(string str);//tom2是i无返回值,有一个string类型参数
        public TOM int tom3(int num, string str);//tom3是int型返回值,有一个int传入参数+一个string型传入参数
      

        static void Main(string[] args)
        {
            tom1 t1 = Test1;
            t1();

            tom2 t2 = Test2;
            tom2 t2_2 = Test2_2;
            t2("tttt22222");
            t2_2("tttt22222");

            tom3 t3 = Test3;
            int num3 = t3(1, "3333tt");
            Console.Read();
        }

       static  void Test1()
        {
            Console.WriteLine("test1");   
        }

        static void Test2(string str)
       {
           Console.WriteLine("test2:"+str);
       }


        static void Test2_2(string str)
        {
            Console.WriteLine("test_2:"+str);
        }
        static int Test3(int num,string str)
        {
            Console.WriteLine("THis is Test3:"+num.ToString()+str);
            return num + 1;
        }

(上述TOM 改为delegate ,即可编译成功,真的吐槽为何中文翻译为委托,真的很拗口,不过,真的很不好定义吧....我也想不到更好的两个字可以概括的)

4.回归官方说委托

  上述TOM即为委托(delegate),为啥我不直接说delegate,直接说delegate,这个东西,真的很难理解。记住,TOM(type of method),方法的类型,再准确的说是定义方法的类型的类型。TOM tom1,这个tom1,则为某具体方法的类型的类型。

接下来用delegate来写吧,但是你内心还是要记住type of method(方法的类型)。

(其实官方讲,我是没啥好讲的,直接推荐两篇博客吧 https://www.cnblogs.com/hushzhang/p/5901052.html)

5.实例应用

        因为最近自己堕入爱河,举个谈恋爱的例子吧。

  1)男女恋爱分手用例。

    需求:女孩纸开心值降低到一定,就会提出分手。或者男孩纸比较渣,男孩纸提出分手。提出分手,

女孩纸会哭泣;男孩纸会变为单身狗。

    步骤:

       ①首先,先定义一个无返回值,无传入参数的委托,SeparateDelegate。  

          public delegate void SeparateDelegate(); 

       ②声明一个女孩纸类,声明一个SeparateDelegate委托的实例mSeparateDel,并在初始化中给mSeparateDel

赋初始(注册)分手回调;女孩纸开心值低于1的时候,触发分手。(女孩纸被动分手,不开心才会分手,因为女孩纸不开心才会分手嘛。)      

 class GirlFriend
    {
        public SeparateDelegate mSeparateDel;
        public string mName { get; set; }
        private int happyNum;
        public int mHappyNum {
            get
            {
                return happyNum;
            }

            set
            {
                happyNum = value;
                if(happyNum<1)
                {
                    Console.WriteLine("我女孩纸,决定了,分手,开始触发分手:");
                    mSeparateDel();
                }
            }
        }

        public GirlFriend(string name)
        {
            mName = name;
            mHappyNum = 80; //刚开始做女朋友肯定很开心嘛
            mSeparateDel = SeparateCallBack;
        }


        void SeparateCallBack()
        {
            Console.WriteLine("我是女孩子,分手了,我大哭...");
        }
    }

        ③声明一个男孩纸类,可以将女孩纸设为男孩纸女朋友,并继续注册女孩纸提出分手可触发的回调;男孩纸可以提出分手。(渣男就是

不一样,分手无理由,各种理由,花心啊,腻了,遇到更漂亮的小姐姐等等,跟开不开心无关系,哈哈哈...)

 class BoyFriend
    {
        public string name { get; private set; }
        public GirlFriend mGirlFriend;
        public BoyFriend(string name,GirlFriend girl)
        {
            this.name = name;
            this.mGirlFriend = girl;
            girl.mSeparateDel += RemoveGirlFriend;
        }

        void RemoveGirlFriend()
        {
            if (this.mGirlFriend != null)
            {
                this.mGirlFriend = null;
            }
            Console.WriteLine("作为男孩纸,很难受,男人不哭,回归单身狗");
        }

        public void PresentSeparate()
        {
            this.mGirlFriend.mSeparateDel();
        }
    }    

      ④实际运行。(保证代码可以跑起来)  

 class Program
    {
        static void Main(string[] args)
        {
            GirlFriend gl = new GirlFriend("XiaoHong Li"); //当然先有女朋友
            BoyFriend mySelf = new BoyFriend("Jack Deng",gl);//才有男朋友
            gl.mHappyNum = 0;//开心值降低,女孩纸自身触发分手
         //   mySelf.PresentSeparate(); //男孩纸主动触发分手
            Console.Read();
        }
    }

  可以看到上述女孩纸自身调用和男孩纸调用出发都可以调用以下运行结果(红框中的,分手可以触发的回调)。

       

6.委托与事件的区别

  还是继续上述用例来说,因为上述用例,触发分手,男孩子和女孩纸都可以触发分手。

  需求:现在因为当今社会是女权主义偏多了,再加上程序员们的忠贞,保证不会触发分手,现在就是要求,设计如此:男孩纸不能触发分手,

只能由女孩纸触发。该怎么做哦。

  分析:

     因为女生的委托实例 mSeparateDel是public的,当然是渣男想调就能调,甚至谁想调,任何时候都能调。那我们把public改为private。

可是矛盾来了,男孩纸也不能注册分手回调了(导致的问题是女孩说分手,男孩不知道,没影响),这样做肯定是不行的。

  解决:

    这时候,事件出来了。为了解决上述问题,只要

    //  public  SeparateDelegate mSeparateDel;
        public event  SeparateDelegate mSeparateDel;

    改成这样,就行了,增加event关键字。就是事件了。

  结果:

    

  编译报错了。渣男不能调用了,但是还是可以+=,注册,是没有问题的。(事件的作用及区别)

  我的理解是,事件与委托的区别:

    事件是一种特殊的委托,事件∈委托,委托∉事件,即事件是委托的子集。

    事件是对委托的一种封装,类似于属性是对字段的封装。

7.感悟

    说来惭愧,笔者现在三年Unity经验,对于委托事件的理解,也是最近才算完全参透的吧。之前真的是看过无数篇文章,一直都是含糊不清,也看了网上

很多关于委托事件的博客,一直真的是,越来越不模糊吧,直至现在,我想,我这一次,真的应该是彻底理解了。(如有不合,欢迎指正。)这其中,尽是自己的

理解,如题所述,通俗说,没有太多的官话。

    2018年12月31日完,终于赶在2018年最后一天,写出一篇自己满意的帖子,尽是自己的理解。

                                                                                                                        

猜你喜欢

转载自www.cnblogs.com/u3ddjw/p/9920994.html