zoukankan      html  css  js  c++  java
  • 委托

    1、什么是委托

      1.1 生活中的委托

      委托,汉语中的解释是把事情托付给别人或别的机构(办理)。生活中的委托挺多的,比如幼儿园,你可以把孩子委托给幼儿园,让他们帮你看管孩子;比如律师,你可以把案件委托给律师,让他帮你打官司;比如快递员,你可以把快递委托他们,让他们帮你送;这些都有一个共同点就是你可以告诉他们你要做什么,让他们去帮你做。下面我们说一下程序中的委托。

      1.2 委托的概念

      委托(Delegate)是C#中的一种类型,它实际上是一个能够持有对某个方法的引用的类。与其它的类不同,委托能够拥有一个签名(signature),并且它"只能持有与它的签名相匹配的方法的引用"。简单来说委托是一个类型,它能接收一个与它签名相同的方法。

    2、委托详解

      2.1 语法

      定义委托需要使用delegate关键字,还需要返回值类型、参数列表。

            delegate 返回值 委托名(可选的参数列表);

      2.2 详解

      上面我们介绍了委托的语法,下面我们来自己定义一个委托。

            // 无参数无返回值
            delegate void NoReturnNoParam();

      看完有的小伙伴可能会有疑问了,这跟定义一个方法差不多啊,除了没有delegate关键字和方法体,那为什么说委托是一个类。单这么看可能看不出来啥,下面我们在反编译工具里面看一下我们上面定义的几个委托生成的IL语言。

      通过上图我们看到了编译器实际上是为我们生成了一个NoReturnNoParam类,继承自System.MulticastDelegate类。它里面包含一个构造函数和BeginInvoke、EndInvoke、Invoke三个方法。现在就很明白了,虽然我们定义的时候很简单,其实编译器都给我们补全了。

    3、委托的使用

      上面我们知道了,委托就是一个类,那我们猜测一下是不是类怎么用,委托就可以怎么用?

      3.1 自定义委托的使用

      首先我们定义一个委托,再定义一个与之方法签名一致的方法。

            // 无参数无返回值委托
            delegate void NoReturnNoParam();
    
            /// <summary>
            /// 无参数无返回值方法
            /// </summary>
            public void NoReturnNoParamMethod()
            {
                Console.WriteLine("This is NoReturnNoParam Method .");
            }

      类使用的时候需要先实例化,那么委托也需要先实例化,但委托实例化的时候必须传递与方法签名相同的方法。

            NoReturnNoParam noReturnNoParam = new NoReturnNoParam(new Example().NoReturnNoParamMethod);
            // 也可以去掉new,直接写方法
            NoReturnNoParam noReturnNoParam1 = new Example().NoReturnNoParamMethod;

      类实例化完了可以调用类中的实例方法,那么委托也一样。上面我们知道了委托类中包含三个方法,下面我们调用一下Invoke方法,Invoke表示执行委托,也就是执行委托传递的方法。调用的时候Invoke可以省略。

            // 执行委托
            noReturnNoParam.Invoke();
            // 省略Invoke版,与上面等价
            noReturnNoParam();

      3.2 异步委托

      通过BeginInvoke和EndInvoke可以实现异步编程。

        3.2.1 BeginInvoke

        BeginInvoke表示开始调用。执行该方法时,会开启新线程,所以不会阻塞主线程。 对于无参数委托,BeginInvoke接收两个参数,AsyncCallback表示异步委托调用结束后的回调方法,如没有回调方法则传null;第二个参数是 object类型的参数,作为额外参数,传入异步委托中,可以是任何值,一般用来标识委托。对于有参数的委托,调用BeginInvoke时需先传入方法参数,再传AsyncCallback类型和object类型参数。该方法还返回一个IAsyncResult对象。

        3.2.2 EndInvoke

        EndInvoke表示结束调用。该方法必须接受一个IAsyncResult对象。要注意的是,一旦调用EndEnvoke方法,就会进入阻塞模式,等待委托执行完毕,当然如果委托已经执行完毕,就可以立马取得结果。

        3.2.3 使用

        异步调用:

                noReturnNoParam.BeginInvoke(null, null);

        异步回调:

            /// <summary>
            /// 异步回调方法
            /// </summary>
            /// <param name="ar"></param>
            private void SendCallBack(IAsyncResult result)
            {
                object obj = result.AsyncState;
                Console.WriteLine($"执行了回调方法,AsyncState:{obj}");
    
                // to do
            }
    
            AsyncCallback asyncCallBack = new AsyncCallback(this.SendCallBack);
            IAsyncResult asyncResult = noReturnNoParam.BeginInvoke(asyncCallBack, "obj");
            noReturnNoParam.EndInvoke(asyncResult);

    4、 内置委托

      C#中内置了两个委托Action和Func,当然既然是委托,那用起来跟我们上面自定义的委托并没有区别。

      4.1 Action

        Action是一个无返回值的委托,它可以接收0-16个泛型的参数。

      4.2 Func

        Func是一个有返回值的委托,它可以接收0-16个泛型参数并返回一个泛型值。

      4.3 用法示例

                // Action
                Action action1 = new DelegateExample().NoReturnNoParamMethod;
                action1.Invoke();
                Action<string> action2 = new DelegateExample().NoReturnWithParamMethod;
                action2.Invoke("string Param");
    
                // Func
                Func<int> func1 = new DelegateExample().WithReturnNoParamMethod;
                int result1 = func1.Invoke();
                Func<string, int> func2 = new DelegateExample().WithReturnWithParamMethod;
                int result2 = func2.Invoke("string Param");

    5、多播委托

      多播委托(MulticastDelegate)的初始化可以像普通委托一样,传入一个签名相同的实例方法。同时,多播委托重载了 += 运算符和 -= 运算符,用来向其调用列表中添加或者删除方法。调用多播委托时,方法将按照添加的顺序被依次调用。

      5.1 添加方法

        下面我们给多播委托添加三个方法,添加完后可通过GetInvocationList方法获取到方法列表,然后依次执行。

            // 多播委托
            var delegateExample = new DelegateExample();
    
            // 添加实例方法1
            MulticastDelegateTest mdTest = new DelegateExample().NoReturnNoParamMethod;
            // 添加实例方法2
            mdTest += delegateExample.NoReturnNoParamMethod_1;
            // 添加静态方法
            mdTest += StaticNoReturnNoParamMethod;
    
            foreach (MulticastDelegateTest item in mdTest.GetInvocationList())
            {
                item.Invoke();
            }

      5.2 删除方法

        既然可以添加,那肯定可以删除,我们直接把上面的+=改为-=,大家可以猜一下下面代码删除了几个方法。

            // 删除实例方法1
            mdTest -= new DelegateExample().NoReturnNoParamMethod;
            // 删除实例方法2
            mdTest -= delegateExample.NoReturnNoParamMethod_1;
            // 删除静态方法
            mdTest -= StaticNoReturnNoParamMethod;
    
            foreach (MulticastDelegateTest item in mdTest.GetInvocationList())
            {
                item.Invoke();
            }

        事实证明之删除了实例方法2和静态方法。因为实例方法1添加和删除的方法对应的实例,在内存中的地址不同,所以删不掉,这点需要注意一下。

      5.3 需要注意的点

      1. 多播委托的返回类型最好定义为void,有返回值时只会返回最后一个方法的返回值。
      2. 删除实例方法时,要删除的方法需要与添加的方法属于同一实例。

    6、事件

      6.1 声明事件

      1. 声明该事件的委托类型
      2. 基于委托定义事件
            // 在类的内部声明事件,首先必须声明该事件的委托类型。
            public delegate void NoReturnNoParam();
            // 基于上面的委托定义事件,使用 event 关键字 
            public event NoReturnNoParam eventRun;

       6.2 事件与委托的区别

      1. 事件是一个委托实例。
      2. 事件不为包容类外部的对象提供对赋值运算符,也就是事件只能出现在+=、-=左边。
      3. 事件确保只有包容类才能触发一个事件通知。

    7、总结

      本节列出委托和事件的用法,涉及代码可至GitHub下载。

  • 相关阅读:
    两种图像缩放算法的对比与实现
    字节流与字符流
    自己的网站 首都易搜网 又修改了一下。。首页改变了
    序列化和反序列化 .NET
    关于 C#异步方法的使用
    vs2010 设计视图中控件无法加载,提示未将对象设置到对象的实例。
    .Net 中的反射(查看基本类型信息) Part.2
    URL重写
    .Net 中的反射(序章) Part.1
    数据库死锁问题 及 解决方法
  • 原文地址:https://www.cnblogs.com/gaozejie/p/10274099.html
Copyright © 2011-2022 走看看