zoukankan      html  css  js  c++  java
  • 第十节:委托和事件(2)(泛型委托、Func和Action、事件及与委托的比较)

    一. 泛型委托

      所谓的泛型委托,即自定义委托的参数可以用泛型约束,同时内置委托Func和Action本身就是泛型委托。

      将上一个章节中的Calculator类中的方法用自定义泛型委托重新实现一下。

     1  public class Calculator2
     2     {
     3         //传统解决方案一:在该类中声明多个方法,分别是加倍、平方、立方的方法
     4 
     5 
     6         //传统解决方案二:在该类中声明一个万能方法,通过传递不同的参数类型来区分是执行加倍还是平方或者立方操作
     7 
     8 
     9         //解决方案三:声明一个万能方法,传递一个委托进来,相当于传递了一个业务逻辑进行,在该方法里只需要执行即可
    10         /// <summary>
    11         /// 万能方法
    12         /// </summary>
    13         /// <param name="arrs">int类型的数组 </param>
    14         /// <param name="mydel">自定义委托</param>
    15         public delegate T myDel<T>(T t);
    16         public static void MySpecMethord<T>(T[] arrs, myDel<T> myDel)
    17         {
    18             for (int i = 0; i < arrs.Length; i++)
    19             {
    20                 arrs[i] = myDel(arrs[i]);
    21                 //arrs[i] = mydel.Invoke(arrs[i]);  //等价于上面那句
    22                 Console.WriteLine(arrs[i]);
    23             }
    24         }
    25 
    26     }

    二. 内置委托

      .Net FrameWork提供两个支持泛型的内置委托,分别是Action和Func,结合lambda表达式,可以提高开发效率

      二者的区别:

      1.Action只能委托无返回值的方法,支持16个重载(in代表输入参数,该重载没有返回值)

      分别是:

        * public delegate void Action<in T>(T obj);

        * public delegate void Action<in T1, in T2>(T1 arg1, T2 arg2);

        * ......

        * public delegate void Action<in T1, in T2,......,in T16>(T1 arg1, T2 arg2,......,T16 arg16);

        *

      2.Func必须委托有返回值的方法,支持17个重载(注意括号里最后一个代表返回值,in代表输入参数,out代表返回值)

      分别是:

        * public delegate TResult Func<out TResult>();

        * public delegate TResult Func<in T, out TResult>(T arg);

        * public delegate TResult Func<in T1, in T2, out TResult>(T1 arg1, T2 arg2);

        * ......

        * public delegate TResult Func<in T1, in T2,.......in T16 out TResult>(T1 arg1, T2 arg2,......T16 arg16);

        *

      总结:除了ref参数和out参数,内置委托基本上能适用于任何泛型委托的场景,非常好用

        3. 内置委托和自定义委托的区别:

      自定义委托需要先声明一下,让系统认识这个自定义委托,然后才能实例化赋值方法;而内置委托系统本事就存在,所有不需要事先声明,直接实例化赋值方法即可

    三. 委托的其它性质

       静态方法和实例方法对于委托的区别,对于静态方法,Target属性为null,所以效率比实例属性更高

     1  public class OtherCharacters
     2     {
     3         public delegate void mySpecDelegate();
     4         /// <summary>
     5         /// 1.测试普通实例方法和静态方法在委托中的区别
     6         /// </summary>
     7         public static void Test1()
     8         {
     9             MR  mr=new MR();
    10             //实例方法
    11             mySpecDelegate a = mr.f1;
    12             Console.WriteLine(a.Target==mr);   //true
    13             //静态方法
    14             mySpecDelegate b = MR.f2;
    15             Console.WriteLine(b.Target==null);  //true
    16         }
    17 
    18     }
    19 
    20     public class MR
    21     {
    22         /// <summary>
    23         /// 普通方法
    24         /// </summary>
    25         public void f1()
    26         {
    27         }
    28         //静态方法
    29         public static void f2()
    30         {
    31 
    32         }
    33     }

    四. 事件

     1. 事件介绍

      * 定义:声明一个委托实例,然后在该实例的前面加上event关键字,就形成事件了

      * 事件的用途:实现广播和订阅的场景

      * 1.广播者:包括1个事件字段,独享执行委托的方法

      * 2.订阅者:通过调用 += 和 -= 来决定何时开始或停止订阅

      * 总结:事件是描述这种场景模式的一个词,事件是委托的一个子集,为了满足“广播/订阅”模式的需求而生

      * 事件和委托的区别:

      * 1. 委托是一种类型,而事件是委托的一个实例,然后在该实例前加上一个关键字

      * 2. 事件有一系列规则和约束用以保证程序的安全可控,事件只有 += 和 -= 操作,这样订阅者只能有订阅或取消订阅操作,没有权限执行其它操作

      * 3. 如果是委托,那么订阅者就可以使用 = 来对委托对象重新赋值(其它订阅者全部被取消订阅),甚至将其设置为null,甚至订阅者还可以直接调用委托,这些都是很危险的操作,广播者就失去了独享控制权

      * 4. 事件保证了程序的安全性和健壮性

     2. 下面代码就事件和委托进行比较

     public class MyEvent
        {
            //下面的案例用委托和事件实现相同的功能
            public Action myDelegate;
            /// <summary>
            /// 触发委托执行的方法
            /// </summary>
            public void realizeDelegate()
            {
                if (myDelegate != null)
                {
                    myDelegate.Invoke();
                }
            }
            public event Action myEvent;
            /// <summary>
            /// 触发事件执行的方法
            /// </summary>
            public void realizeEvent()
            {
                if (myEvent != null)
                {
                    myEvent.Invoke();
                }
            }
    
            #region 供委托和事件测试调用的方法
    
            public static void T1()
            {
                Console.WriteLine("方法一");
            }
    
            public static void T2()
            {
                Console.WriteLine("方法二");
            }
    
            public static void T3()
            {
                Console.WriteLine("方法三");
            }
    
            #endregion
    
        }

     3. 调用时候的区别:

     委托中的订阅者可以直接Invoke()来调用委托,而事件中的订阅者不能直接Invoke()调用委托,只能通过广播者中的方法来实现调用委托,从而保证广播者独享控制权。

     1  {
     2                 Console.WriteLine("--------------------------七. 事件------------------------------------");
     3                 MyEvent m1 = new MyEvent();
     4                 //1. 委托实现
     5                 Console.WriteLine("--------------------------1. 委托实现------------------------------------");
     6                 //订阅者进行订阅
     7                 m1.myDelegate += MyEvent.T1;
     8                 m1.myDelegate += MyEvent.T2;
     9                 m1.myDelegate += MyEvent.T3;
    10                 m1.myDelegate.Invoke();  //委托中的订阅者可以直接调用委托
    11                 m1.myDelegate -= MyEvent.T2;
    12                 m1.realizeDelegate();
    13                 //2. 事件实现
    14                 Console.WriteLine("--------------------------2. 事件实现------------------------------------");
    15                 m1.myEvent += MyEvent.T1;
    16                 m1.myEvent += MyEvent.T2;
    17                 m1.myEvent += MyEvent.T3;
    18                 // m1.myEvent.Invoke();    //事件中的订阅者不能直接调用委托
    19                 m1.realizeEvent();         //只能通过发布者中方法来实现委托,保证发布者独享控制权
    20                 m1.myEvent -= MyEvent.T2;
    21                 m1.realizeEvent();  
    22 
    23             } 

      结果:

  • 相关阅读:
    关于Java的代理模式
    关于Java串行、并行执行——使用Callable多线程
    关于区域表system_district:省市县街道四级地址表
    关于MongoDB在windows下安装
    关于Eureka 服务注册列表显示IP问题研究
    关于开发APP接口版本不兼容的问题
    关于MySQL创建数据库字符集和数据库排序规则的对比选择
    关于MySQL的行转列
    关于MySQL统计一列中不同值的数量方法
    关于Java 8 forEach
  • 原文地址:https://www.cnblogs.com/yaopengfei/p/6985491.html
Copyright © 2011-2022 走看看