菜鸟之旅——认识委托与事件

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

  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)

总结

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

posted on 2018-01-16 09:38  愉悦的绅士  阅读(1354)  评论(7编辑  收藏  举报