温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

C#forUnity快速入门(连载15)_C#委托与事件

发布时间:2020-06-25 21:06:54 来源:网络 阅读:19998 作者:Liu_guozhu 栏目:开发技术

C# for Unity编程语言快速入门教程(连载15)_C#委托与事件



    C#的委托(delegate)与事件(event)  其实不是一个容易理解的技术,而且很多C#书籍作者还经常把它与“观察者设计模式”(Observer模式)放在一起进行讨论(注:因为委托与事件是“Observer”设计模式的一种很好的实现方式),其实这就进一步增加了对于C#初学者的学习难度。


  所以笔者打破常规,先从讲故事的方式,先来介绍"委托”。


案例故事:“老板来啦!”
       2007年是中国股市非常火热的一年,某公司办公室每个员工都在热心“炒股”,为了防止老板看见,所以大家让“办公室前台”小张来注意老板;每当老板进入办公室的时候,则小张就给每个好友QQ通知,“老板来啦!” 


以上案例,编写如下代码:

    class Program
    {
        public void InformMrZhangSan(string name)
        {
            Console.WriteLine("{0},老板来了,张三,请关闭电脑", name);
        }

        public void InformMrLisi(string name)
        {
            Console.WriteLine("{0},老板来了,李四,请关闭电脑", name);
        }

        public void InformMrWangWu(string name)
        {
            Console.WriteLine("{0},老板来了,王五,请关闭电脑", name);
        }

        /// <summary>
        /// (前台)小张发信息
        /// </summary>
        public void MrZhangSendInfo()
        {
            InformMrZhangSan("我是前台小张");
            InformMrLisi("我是前台小张");
            InformMrWangWu("我是前台小张");
        }

        static void Main1(string[] args)
        {
            Program obj = new Program();
            obj.MrZhangSendInfo();
        }
    }


    以上代码,就是一个典型的同步代码实现,也就是调用一个类型的方法,会即刻出现该方法执行的结果。相信大家很好理解以上代码。

   问题来了,此时如果公司有很多员工,都要求“前台小张”来发QQ(单独)通知给他。当然还要想到,可能公司还会有新的员工加入“炒股”的队伍,也要求“前台小张”来进行“提醒”服务,这如何处理呢?

  我们先考虑案例故事中的解决方案: 大家一定能够想到,其实很简单,可以建立一个“QQ提醒群”,需要提醒的公司员工,可以提前加入到这个群中。当老板进入办公室的时候,前台小张只需要在Q群里发布一条消息:“老板来了,注意啦!”即可,这样无论公司员工不断加入,还有有人离开这个群,对于小张来说是不受任何影响的。

  我们按照以上的思路,重写以上类,代码如下所示:


   //定义委托: 通知信息
    public delegate void InformInfoHandler(string name);

    //定义类
    class UseDeledate
    {
        public void InformMrZhangSan(string name)
        {
            Console.WriteLine("{0},老板来了,张三,请关闭电脑", name);
        }

        public void InformMrLisi(string name)
        {
            Console.WriteLine("{0},老板来了,李四,请关闭电脑", name);
        }

        public void InformMrWangWu(string name)
        {
            Console.WriteLine("{0},老板来了,王五,请关闭电脑", name);
        }

        //新来的同事
        public void InformMrNewComer(string name)
        {
            Console.WriteLine("{0},老板来了,新来的同事,请关闭电脑", name);
        }


        /// <summary>
        /// 通知所有人
        /// </summary>
        /// <param name="name"></param>
        /// <param name="informInfoDelegate"></param>
        public void InformAll(string name,InformInfoHandler informInfoDelegate)
        {
            informInfoDelegate(name);
        }


        /// <summary>
        /// (前台)小张发信息
        /// </summary>
        public void MrZhangSendInfo()
        {
            //定义委托(很类似“定义类”)
            InformInfoHandler myInform = new InformInfoHandler(InformMrZhangSan);
            myInform += new InformInfoHandler(InformMrLisi);
            myInform += new InformInfoHandler(InformMrWangWu);
            myInform += new InformInfoHandler(InformMrNewComer);

            InformAll("我的前台小张",myInform);
        }

        static void Main(string[] args)
        {
            UseDeledate obj = new UseDeledate();
            obj.MrZhangSendInfo();
        }
    }


  在以上示例代码中,我们先定义了一个委托:public delegate void InformInfoHandler(string name); 这个委托的类型,必须与需要注册的方法“签名”(方法的参数列表与返回类型)一致。然后我们定义类:“UseDeledate” ,在这个类中,方法MrZhangSendInfo()就是负责方法的“注册”,这里就相当于,在小张通知Q群前,需要让需要的公司员工提前“入群”。

   方法 InformAll() 的方法列表中,我们引入了委托类型: InformInfoHandler ,通过这个委托,来间接调用注册的所有方法(一共4个)。这样无论调用的方法是一个还是很多,调用方法只是用 “informInfoDelegate(name); ” 这个委托即可完成任务,实现了“调用方”与(实现功能)“方法体”本身的解耦和。

 

 以上代码的写法主要是考虑与故事的配合,在这给出更加一般的委托定义编写方式示例:

   //定义委托(通知信息)
    public delegate void InformInfoHandler(string name);

    class UseDeletage2
    {
        //声明委托类型
        InformInfoHandler myInform;

        public UseDeletage2()
        {
            //委托注册()
            myInform = new InformInfoHandler(InformMrZhangSan);
            myInform += new InformInfoHandler(InformMrLisi);
            myInform += new InformInfoHandler(InformMrWangWu);
            myInform += new InformInfoHandler(InformMrNewComer);

            ////委托取消注册
            //myInform -= new InformInfoHandler(InformMrWangWu);

            //直接用“=”赋值符号,则仅最后注册的方法有效。
            //myInform = new InformInfoHandler(InformMrZhangSan);
            //myInform = new InformInfoHandler(InformMrLisi);
            //myInform = new InformInfoHandler(InformMrWangWu);
            //myInform = new InformInfoHandler(InformMrNewComer);
        }


        public void InformMrZhangSan(string name)
        {
            Console.WriteLine("{0},老板来了,张三,请关闭电脑", name);
        }
        public void InformMrLisi(string name)
        {
            Console.WriteLine("{0},老板来了,李四,请关闭电脑", name);
        }
        public void InformMrWangWu(string name)
        {
            Console.WriteLine("{0},老板来了,王五,请关闭电脑", name);
        }
        //新来的同事
        public void InformMrNewComer(string name)
        {
            Console.WriteLine("{0},老板来了,新来的同事,请关闭电脑", name);
        }


        /// <summary>
        /// 通知所有人(此步骤可以省略)
        /// </summary>
        /// <param name="name"></param>
        /// <param name="informInfoDelegate"></param>
        //public void InformAll(string name, InformInfoHandler informInfoDelegate)
        //{
        //    informInfoDelegate(name);
        //}


        /// <summary>
        /// (前台)小张发信息
        /// </summary>
        public void MrZhangSendInfo()
        {
            //InformAll("我的前台小张", myInform); //省略
            myInform("我是前台的小张");
        }

        static void Main1(string[] args)        
        {
            Console.WriteLine("UseDeletage2 测试");
            UseDeletage2 obj = new UseDeletage2();
            obj.MrZhangSendInfo();
        }
    }



委托定义方式可以总结如下四个步骤。
A)  定义委托
B) 声明委托实例。
C) 委托注册(注册方法)
D) 调用委托


现在总结委托的一些普遍规则:


规则1: 委托本质上就是一个“类”,能定义类的地方,都可以定义委托。
       (可以通过反编译软件,进行查看证明,委托就是继承了 [mscorlib]System.MulticastDelegate 的特殊类)

规则2: 使用委托技术,可以实现在“在方法的参数中传方法”。
        即: 把一个方法作为参数,在另一个方法中进行调用。
        好处是: 大大减少程序中逻辑判断的分支。
                     
规则3: 多播委托(“委托链”):  委托可以通过不断的进行“注册方法”,实现一次性调用多个方法。   

规则4:在方法中调用委托,比方法中直接调用方法的好处是:
            可以在不修改调用方法的前提下,不断的增加业务功能。
            以此可以更加灵活的处理逻辑,减少逻辑判断。

规则5:  委托技术可以实现“调用方”与“实现方”的解耦合。


规则6: 可以通过+= 与-= 来给“多播委托”(委托链),来
              动态的添加注册方法与取消注册方法。
        
规则7: 直接用“=”赋值符号,则仅最后注册的方法有效。
              则就不是“多播委托”(或者说“委托链”)了,可以起到“阻止”的作用。
 
规则8: 委托本质就是方法的地址。(类似C++ 语言中的“函数指针”)



现在给出一个“事件”的示例,然后总结一下委托与事件的区别:


    class UseEvent
    {
        //声明委托类型
        //InformInfoHandler myInform;

        //声明事件
        public event InformInfoHandler eveMyInform;

        public UseEvent()
        {
            //事件的注册()
            //eveMyInform += new InformInfoHandler(InformMrZhangSan);
            //eveMyInform += new InformInfoHandler(InformMrLisi);
            //eveMyInform += new InformInfoHandler(InformMrWangWu);
            //eveMyInform += new InformInfoHandler(InformMrNewComer);

            //简化写法
            eveMyInform += InformMrZhangSan;
            eveMyInform += InformMrLisi;
            eveMyInform += InformMrWangWu;
            eveMyInform += InformMrNewComer;
        }

        public void InformMrZhangSan(string name)
        {
            Console.WriteLine("{0},老板来了,张三,请关闭电脑", name);
        }
        public void InformMrLisi(string name)
        {
            Console.WriteLine("{0},老板来了,李四,请关闭电脑", name);
        }
        public void InformMrWangWu(string name)
        {
            Console.WriteLine("{0},老板来了,王五,请关闭电脑", name);
        }
        //新来的同事
        public void InformMrNewComer(string name)
        {
            Console.WriteLine("{0},老板来了,新来的同事,请关闭电脑", name);
        }

        /// <summary>
        /// (前台)小张发信息
        /// </summary>
        public void MrZhangSendInfo()
        {
            eveMyInform("我是前台的小张");
        }

        static void Main1(string[] args)
        {
            Console.WriteLine("UseEvent 测试");
            UseEvent obj = new UseEvent();
            obj.MrZhangSendInfo();
        }
    }


按照以上示例,我们总结“委托”与“事件"的区别:
  A) “事件”本质上就是委托的一个实例
  B)  委托可以定义在类的外部,事件只能定义在类的内部。


    以上代码是事件“调用方”与“功能实现方”都定义在了同一个类中,是不具备“实战”价值的,所以笔者给出如下在商业项目中应用的示例,供大家参考:



    /// <summary>
    /// 委托: 通知信息
    /// </summary>
    /// <param name="name"></param>
    public delegate void InformInfoHandler(string name);

   

    //调用方

    class InvokeClass
    {
        //声明事件
        public static event InformInfoHandler eveMyInform;

        /// <summary>
        /// 显示通知信息
        /// </summary>
        public void DisplayInformInfo()
        {
            if (eveMyInform!=null)
            {
                //eveMyInform("我是前台");
                eveMyInform.Invoke("我是前台");
            }
        }

    }
 

    //业务功能实现方,以及“事件注册” 

    public class RegisterClass
    {
        public RegisterClass()
        {
            InvokeClass.eveMyInform += InfomrZhangSan;
            InvokeClass.eveMyInform += InfomrLisi;
        }

        void InfomrZhangSan(string name)
        {
            Console.WriteLine("{0},张三,上午好",name);
        }

        void InfomrLisi(string name)

        {
            Console.WriteLine("{0},李四,下午好", name);
        }
    }
 


    //测试类 

    class TestClass
    {
        /// <summary>
        /// 测试方法
        /// </summary>
        public static void Main()
        {
            //注册事件
            new RegisterClass();

            //调用方
            InvokeClass obj = new InvokeClass();
            obj.DisplayInformInfo();

        }
    }


    好了,以上代码大家如果可以看懂,就可以认为基本“懂”了委托与事件的基本使用,现在我们再引申性总结一下:


什么是“观察者模式”(Observer)

   观察者模式又叫做发布-订阅(Publish/Subscribe)模式、模型-视图(Model/View)模式、源-监听器(Source/Listener)模式或从属者(Dependents)模式。观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态上发生变化时,会通知所有观察者对象,使它们能够自动更新自己。


  这个委托与事件,笔者引申一点,帮助大家进一步思考:

  我们在开发一个软件系统(游戏项目)时,常常要求在某一个对象的状态发生变化的时候,某些其它的对象做出相应的改变。例如: 我们游戏开发中,击中一架敌机,则屏幕UI的相应分数,要更新数值。做到这一点的设计方案有很多,但是为了使系统能够易于复用,应该选择低耦合度的设计方案。减少对象之间的耦合有利于系统的复用,并且这些低耦合的对象之间需要维持行动协调一致,保证很好的协作性。这样“观察者模式”是满足这一要求的最佳设计方案之一。


向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI