由浅入深理解委托与事件

引言

  看过上一篇文章的朋友应该知道,要想给一个类添加自定义事件,首先要添加一个委托,然后通过这个委托声明一个事件,在主程序中其他类只要有符合那个委托的方法,就可以用这个方法订阅那个自定义事件,当自定义事件发布时,就会调用委托的方法,这其中的指导思想就是发布-订阅模型。可是,大家有没有觉得,当我们在自定义类中调用事件后,它居然就能自动调用订阅这个事件的所有方法,这个过程很神奇。今天,我们就来尝试着揭开这层神秘的面纱。

委托

  首先,什么是委托,委托在我的个人理解中相当于C中的函数指针,但它跟函数指针在使用方式上还是有些不太一样。用微软官方的话讲,在 .NET 中委托提供后期绑定机制,后期绑定意味着调用方在你所创建的算法中至少提供一个方法来实现算法的一部分(摘自微软教程)。我建议微软教程大家随意瞄一眼就好,从我的角度看就不像在说人话,我们还是来看看民间对于委托的介绍。


 

  什么是委托?
  首先要知道什么是委托,用最通俗易懂的话来讲,你就可以把委托看成是用来执行方法(函数)的一个东西。
  如何使用委托?
  在使用委托的时候,你可以像对待一个类一样对待它。即先声明,再实例化。只是有点不同,类在实例化之后叫对象或实例,但委托在实例化后仍叫委托。

  委托是一个类,它定义了方法的类型,使得可以将方法当作另一个方法的参数来进行传递,这种将方法动态地赋给参数的做法,可以避免在程序中大量使用If-Else(Switch)语句,同时使得程序具有更好的可扩展性。我们知道委托是一个引用类型,所以它具有引用类型所具有的通性。它保存的不是实际值,而是保存对存储在托管堆(managed heap)中的对象的引用。那它保存的是对什么的引用呢?委托保存的是对函数(function)的引用(从水平线开始均摘自百度百科)。我认为百度百科解释的非常好,包括它后面写的对于学习过C/C++的人来说与C中的函数指针的区别,不过我没放上来因为有点长,有兴趣的大家可以跳转链接看。

代码释义

  可能看完定义,大家还是不太懂,那么我将结合代码再讲一次,当然熟悉委托的朋友可以跳过这一小节。

  我们可以把委托的写法和使用与类的写法和使用联系起来,这样更方便自己理解。可以先看下这段简单代码:

  public class Program
    {
        //写一个委托,可以理解为就是定义了一个方法类型的模板,这个模板名就叫做SayHelloWay(可以类比写一个类,然后类就是生成一堆数据和方法的模板)
        public delegate void SayHelloWay();

        public static void SayHelloByConsole()
        {
            Console.WriteLine("Hello");
        }

        public static void SayHelloByDiagnostics()
        {
            System.Diagnostics.Debug.WriteLine("Hello");
        }

        static void Main(string[] args)
        {
            //可以像创建一个类的实例一样,创建一个委托,但这个委托的构造函数需要一个方法参数
            SayHelloWay sayHello = new SayHelloWay(SayHelloByConsole);
            //在委托后面添加()即可调用在构造函数构造时传入的方法即SayHelloByConsole,可以理解这个方法就是委托的方法
            sayHello(); //可以像调用方法一样使用委托,即括号里可以传入参数(前提方法声明时本来参数列表就有参数)
            sayHello = new SayHelloWay(SayHelloByDiagnostics);
            sayHello();
        }
    }

//控制台结果:Hello
//VS输出面板结果:Hello

既然委托是定义某一种方法的模板,那么我们完全可以拿这个模板作为某个方法的参数类型,这样就可以往这个方法里传入方法,于是回调函数就来了。

    public class Program
    {
        public delegate void SayHelloWay();

        public static void SayHelloByConsole()
        {
            Console.WriteLine("Hello");
        }

        public static void SayHelloByDiagnostics()
        {
            System.Diagnostics.Debug.WriteLine("Hello");
        }

        public static void SayHello(SayHelloWay say)
        {
            say();
        }

        static void Main(string[] args)
        {
            SayHello(SayHelloByConsole);
        }

其实创建委托实例的方式不止new这一种,以下我再演示下创建委托实例的另外两种方式,分别是直接赋值和lambda表达式

        static void Main(string[] args)
        {
            //直接赋值
            SayHelloWay sayhello = SayHelloByDiagnostics;
            sayhello();
            //lambda表达式赋值,也叫匿名委托
            SayHelloWay sayhello1 = () =>
            {
                Console.Write("hello by lambda");
            };
            sayhello1();
        }

委托可以被订阅多个方法,也就是说,一个委托身上可以有多个方法,调用委托时,会触发它身上绑定的所有方法。只需要给委托“+=”或者“+”新的方法名即可,这种绑定多个方法的委托就叫做多播委托,如下代码所示:

        static void Main(string[] args)
        {
            SayHelloWay sayHello = SayHelloByConsole;
           // sayHello = sayHello + SayHelloByDiagnostics;  这种不常用
            sayHello += SayHelloByDiagnostics;  //这种常用些
            sayHello();
        }

如果想在多播委托中去除一个方法,可以使用“-”或者“-=”,这里不做演示,因其用法与“+=”或者“+”相同。

 事件

 

posted @ 2022-12-08 00:00  hlz2516  阅读(70)  评论(0)    收藏  举报