zoukankan      html  css  js  c++  java
  • 菜鸟之旅——认识委托与事件

      这个是我刚入行第一个感兴趣的知识点,翻了翻不知道什么时候记的笔记,觉得有必要把自己学的知识在梳理一遍,有一些点比当时更清晰;在这里,先将一位大牛的博客贴出来,有兴趣的可以去看看,很详细的介绍了委托与事件,读了几遍受益匪浅:

      C# 中的委托和事件  C#中的委托和事件(续)

    本文实例是在VS2013下实现的

    初识委托

      刚刚学习委托可能会有些困惑,不知道委托是什么,怎么用,有什么好处,下面我会通过一个简单的场景来进行说明。

      场景:假设下某人班回到家,依次打开灯,打开热水器,打开电视。

      我们先定义一个房子:

        public class House
        {
            public void OpenLight()
            {
                Console.WriteLine("打开灯!");
            }
            public void OpenHeater()
            {
                Console.WriteLine("打开热水器!");
            }
            public void OpenTV()
            {
                Console.WriteLine("打开电视!");
            }
        }

     将方法当作参数传递

      下面我们先利用委托来进行执行开灯、开热水器、开电视的操作,我们先定义一个无参数无返回值的委托(事实上.NET已经有一个内置的Action委托),在创建一个打开电器的方法:

        delegate void OpenMachineHandle();
        static void OpenMachine(OpenMachineHandle openHandle)
        {
            openHandle();
        }

      然后我们就可以这样来实现打开电器:

        static void Main(string[] args)
        {
            House house = new House();
            Console.WriteLine("开门回家......");
            OpenMachine(house.OpenLight);
            OpenMachine(house.OpenHeater);
            OpenMachine(house.OpenTV);
    
            Console.ReadLine();
        }

      我们可以看到我们是将House的三个实例方法当作参数来进行传递,不过有人会说,这个我们可以直接调用方法来进行实现啊?你看这还多了一个OpenMachine的方法不是?现在假设不是用委托,并且只能用OpenMachine的方法打开电器(这种的需求会有的,比方说OpenMachine是我作为第三方发布的接口),里面使用了if-else或switch来进行分辨;但是,改需求了XD,增加打开洗衣机了,这样子我的接口也需要变化以适应新的需求,再发布,需求再变动那岂不是改的没完没了?

      这里我们直接打开电器方法当作参数来使用,以后增加新的打开电器犯法就可以不用修改OpenMachine这个统一入口,只是在主程序里面增加一行调用即可;这样子可以在某些场景下还可以减少使用if-else或switch语句,在一定程度上解耦程序,使得程序具有更好的可扩展性

     委托绑定多个方法

      当然,委托也可以绑定多个方法(静态方法、实例方法都可以,只要参数与返回值与委托的一样即可),以上程序我们还可以这么改,我们可以无视掉OpenMachine这个统一入口,然后实例化一个委托,直接将打开电器方法直接绑定到委托上面,然后让这个实例代我们执行所有绑定好的方法,实例化还有另外的方法,不过+=是增加绑定,-=是移除绑定,这些是一定的。

        static void Main(string[] args)
        {
            House house = new House();
            Console.WriteLine("开门回家......");
            OpenMachineHandle openHandle = house.OpenLight;
            openHandle += house.OpenHeater;
            openHandle += house.OpenTV;
            openHandle();
    
            Console.ReadLine();
        }

    从委托到事件

      在面向对象中,讲究对对象的封装,我们可以将delegate的实例封装到一个类中,这样可以更方便的在客户端中使用,这里建立一个Person类:

        public class Person
        {
            public string Name { get; set; }
    
            public OpenMachineHandle openMachineHandle;
    
            public void OpenMachine()
            {
                if (openMachineHandle != null)
                    openMachineHandle();
            }
        }

      我们就可以这样使用委托:

        static void Main(string[] args)
        {
            House house = new House();
            Person caster = new Person();
            caster.Name = "Caster";
            caster.openMachineHandle = house.OpenLight;
            caster.openMachineHandle += house.OpenHeater;
            caster.openMachineHandle += house.OpenTV;
    
            Console.WriteLine("{0}开门回家......", caster.Name);
            caster.OpenMachine();
    
            Console.ReadLine();
        }

      看到这里大家可能会觉得稍微有些问题,openMachineHandle第一次绑定方法是=赋值语法,后面的才会变为+=注册的方式,还有这里直接使用了openMachineHandle这一个委托变量,但是在面向对象中,不是所有的成员变量都可以公布出来的,对于普通的成员变量来说,可以使用属性来进行封装,但是委托变量使用属性还是解决不了第一个问题,于是就可以使用事件event来对委托进行封装了,这样,我们改写Person类:

        public class Person
        {
            public string Name { get; set; }
    
            public event OpenMachineHandle OpenMachine;
    
            public void OpenDoor()
            {
                OpenMachine();
            }
        }

      声明事件的同时会默认构造一个私有的OpenMachineHandle委托(就行上面声明一个Name属性时会默认构造一个私有变量一样),详细可以翻阅上面大牛的博客,我也正在研究中;改写完毕之后,我们再重新在主程序里面使用:

        static void Main(string[] args)
        {
            House house = new House();
            Person caster = new Person();
            caster.Name = "Caster";
            caster.OpenMachine += house.OpenLight;
            caster.OpenMachine += house.OpenHeater;
            caster.OpenMachine += house.OpenTV;
                
            Console.WriteLine("{0}开门回家......", caster.Name);
            caster.OpenDoor();
    
            Console.ReadLine();
        }

      这里我们就可以直接使用+=注册方法来绑定想要执行的事件,前面我说过OpenMachineHandle这个委托是无参数无返回值的,那么OpenMachineHandle这个可以完全的换成Action这一个内置委托。
      

    .NET内置委托介绍

     delegate关键字

      delegate我们常用到的一种声明
      delegate至少0个参数,至多32个参数,可以无返回值,也可以指定返回值类型。
      例:public delegate int MethodtDelegate(int x, int y);表示有两个参数,并返回int型。

      Action

      Action是无返回值的泛型委托。
      Action 表示无参,无返回值的委托
      Action<int,string> 表示有传入参数int,string无返回值的委托
      Action<int,string,bool> 表示有传入参数int,string,bool无返回值的委托
      Action<int,int,int,int> 表示有传入4个int型参数,无返回值的委托
      Action至少0个参数,至多16个参数,无返回值。
       例:
            public void Test<T>(Action<T> action,T p)
            {
                action(p);
            }

     Func

      Func是有返回值的泛型委托
      Func<int> 表示无参,返回值为int的委托
      Func<object,string,int> 表示传入参数为object, string 返回值为int的委托
      Func<object,string,int> 表示传入参数为object, string 返回值为int的委托
      Func<T1,T2,,T3,int> 表示传入参数为T1,T2,,T3(泛型)返回值为int的委托
      Func至少0个参数,至多16个参数,根据返回值泛型返回。必须有返回值,不可void
      例:   
            public int Test<T1,T2>(Func<T1,T2,int>func,T1 a,T2 b)
            {
                return func(a, b);
            }

     predicate

      predicate 是返回bool型的泛型委托
      predicate<int> 表示传入参数为int 返回bool的委托
      predicate有且只有一个参数,返回值固定为bool
      例:public delegate bool Predicate<T> (T obj)

    总结

      我对于委托的学习总结就暂时到此结束,东西不是很多,更深层的代码实现和设计思想还有待学习,希望能给对于委托还不了解的人有一定的帮助!

  • 相关阅读:
    git命令log与reflog的比较
    git基础仓库提交到新仓库,保存老仓库历史,并同步老仓库跟新到新仓库中
    classpath*与classpath
    fastjson将对象和json互转,@JSONField的使用及不生效
    feign接口自动生成工具
    IIS .Net Core 413错误和Request body too large解决办法
    thinphp 上传文件到七牛
    php 整合微信、支付宝扫码付款
    Jenkins:整合SonarQube8
    Jenkins:流水线打包运行boot项目
  • 原文地址:https://www.cnblogs.com/nbclw/p/8295038.html
Copyright © 2011-2022 走看看