一. 泛型委托
所谓的泛型委托,即自定义委托的参数可以用泛型约束,同时内置委托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 }
结果: