第九节:委托和事件(1)(委托的发展历史、插件式编程、多播委托)
一. 委托的發展歷史和基本用法
說起委托,每個人可能都會對他有不同的理解,結合實戰中委托的使用,我對其理解是:委托和類一樣,是用戶的一個自定義類型,委托可以有參數、有返回值,委托的關鍵字是delegate,委托是方法的抽象,有了委托的存在,使得方法可以作為參數傳遞給另一個方法,同時調用委托的時候,委托所包含的所有方法都會被實現。
委托的寫法由繁瑣→簡潔,經歷了好多過程,大致概括為:new實例化傳遞方法→直接等于方法名→new實例化中傳遞匿名方法→省略匿名方法關鍵字→可以去掉大括號和分號 等等。
代碼如下:
? ? ? ?
1 public class MyDelegate2 {3 //1. 委托的聲明4 public delegate void NoReturnNoPara();5 public delegate int WithReturnNoPara();6 public delegate void NoReturnWithPara(int id, string name);7 public delegate MyDelegate WithReturnWithPara(DateTime time);8 9 10 //2. 委托的使用(在show方法中調用) 11 public void Show() 12 { 13 //以“有參無返回值委托”為例,介紹委托的各種用法 14 //2.1 用法一 15 { 16 NoReturnWithPara methord = new NoReturnWithPara(this.Test1); 17 methord.Invoke(1, "唐馬儒1"); 18 } 19 //2.2 用法二 20 { 21 NoReturnWithPara methord = this.Test1; 22 methord.Invoke(2, "唐馬儒2"); 23 } 24 //2.3 用法三 DotNet 2.0 時代 25 { 26 NoReturnWithPara methord = new NoReturnWithPara 27 ( 28 delegate(int id, string name) 29 { 30 Console.WriteLine("{0} {1}", id, name); 31 } 32 ); 33 methord.Invoke(3, "唐馬儒3"); 34 } 35 //2.4 用法四 DotNet 3.0 時代 36 { 37 NoReturnWithPara methord = new NoReturnWithPara 38 ( 39 (int id, string name) => 40 { 41 Console.WriteLine("{0} {1}", id, name); 42 } 43 ); 44 methord.Invoke(4, "唐馬儒4"); 45 } 46 //2.5 用法五 委托約束 47 { 48 NoReturnWithPara methord = new NoReturnWithPara 49 ( 50 (id,name) => 51 { 52 Console.WriteLine("{0} {1}", id, name); 53 } 54 ); 55 methord.Invoke(5, "唐馬儒5"); 56 } 57 //2.6 用法六 (如果方法體只有一行,可以去掉大括號和分號) 58 { 59 NoReturnWithPara methord = new NoReturnWithPara((id, name) => Console.WriteLine("{0} {1}", id, name)); 60 methord.Invoke(6, "唐馬儒6"); 61 } 62 //2.7 用法七 63 { 64 NoReturnWithPara methord = (id, name) => Console.WriteLine("{0} {1}", id, name); 65 methord.Invoke(7, "唐馬儒7"); 66 methord(7, "唐馬儒7"); 67 } 68 //2.8 用法八 69 { 70 //Func<int, bool> methord = (x) => 71 // { 72 // return x > 6; 73 // }; 74 //等價于(原理,當只有一個參數的時候,可以省略參數的小括號,當方法體只有一行的時候,可以省略大括號和分號,即lambda形式) 75 Func<int, bool> methord = x => x > 6; 76 Console.WriteLine(methord.Invoke(8)); 77 } 78 79 } 80 private void Test1(int id, string name) 81 { 82 Console.WriteLine("{0} {1}", id, name); 83 } 84 85 private void Test2() 86 { 87 Console.WriteLine("DoNothing"); 88 } 89 90 private void Test3() 91 { 92 Console.WriteLine("DoNothing"); 93 } 94 }二. 解耦、插件式編程
1. 背景:有一個CarFactory工廠類,可以建造自然吸氣發動機、渦輪增壓發動機、電動發動機,至于要建造哪個發動機,有多種處理方案。
?方案(一):通過傳遞不同的參數來建造不同的發動機
?? 原理:傳遞一個參數,根據參數類型來執行不同的邏輯
缺點:如果增加新的發動機類型,需要修改BuildEngine方法中的內部邏輯,不符合開閉原則
代碼如下:
1 /// <summary>2 /// 這里建一個Car工廠,用來建造三種不同類型的發動機3 /// </summary>4 public class CarFactory5 {6 /// <summary>7 /// 方案一:通過傳遞不同的參數來建造不同的發動機8 /// 原理:傳遞一個參數,根據參數類型來執行不同的邏輯9 /// 缺點:如果增加新的發動機類型,需要修改BuildEngine方法中的內部邏輯,不符合開閉原則 10 /// </summary> 11 /// <param name="type">參數類型</param> 12 public static void BuildEngine(EngineType type) 13 { 14 if (type == EngineType.NaturalInspiration) 15 { 16 Console.WriteLine("建造自然吸氣發動機"); 17 } 18 else if (type == EngineType.Turbo) 19 { 20 Console.WriteLine("建造渦輪增壓發動機"); 21 } 22 else if (type == EngineType.Electric) 23 { 24 Console.WriteLine("建造電動發動機"); 25 } 26 } 27 28 /// <summary> 29 /// 發動機類型的枚舉類 30 /// </summary> 31 public enum EngineType 32 { 33 NaturalInspiration = 0, //自然吸氣 34 Turbo = 1, //渦輪增壓 35 Electric = 2 //電動 36 } 37 }調用:
1 //1.傳統的方式,通過參數類型來區分 2 Console.WriteLine("--------------------------1.傳統的方式,通過參數類型來區分------------------------------------"); 3 CarFactory.BuildEngine(EngineType.NaturalInspiration); 4 CarFactory.BuildEngine(EngineType.Turbo); 5 CarFactory.BuildEngine(EngineType.Electric);結果:
方案(二):通過傳遞委托來建造不同的發動機
原理:傳遞一個邏輯給我,我去執行
優點:如果增加新的發動機,只需要單獨新增對應建造發動機的方法即可,不需要改變BuildEngine2的內部邏輯,符合開閉原則
? ?代碼如下:
1 public class CarFactory2 {3 /// <summary>4 /// 方案二:通過傳遞委托來建造不同的發動機5 /// 原理:傳遞一個邏輯給我,我去執行6 /// 優點:如果增加新的發動機,只需要單獨新增對應建造發動機的方法即可,不需要改變BuildEngine2的內部邏輯,符合開閉原則7 /// </summary>8 public static void BuildEngine2(BuildEngineDel be)9 { 10 be.Invoke(); 11 } 12 //聲明一個無參數的委托 13 public delegate void BuildEngineDel(); 14 //下面三個是建造不同發動機的方法 15 public static void BuildNatural() 16 { 17 Console.WriteLine("建造自然吸氣發動機"); 18 } 19 public static void BuildTurbo() 20 { 21 Console.WriteLine("建造渦輪增壓發動機"); 22 } 23 public static void BulidElectric() 24 { 25 Console.WriteLine("建造電動發動機"); 26 } 27 28 } 29 }調用:
1 //2.將委托當做參數,進行解耦,實現插件式編程(案例一) 2 Console.WriteLine("--------------------------2.將委托當做參數,進行解耦,實現插件式編程(案例一)------------------------------------"); 3 //將方法賦給委托,將委托當做參數傳遞給新方法 4 CarFactory.BuildEngineDel be1 = CarFactory.BuildNatural; 5 CarFactory.BuildEngine2(be1); 6 CarFactory.BuildEngineDel be2 = CarFactory.BuildTurbo; 7 CarFactory.BuildEngine2(be2); 8 CarFactory.BuildEngineDel be3 = CarFactory.BulidElectric; 9 CarFactory.BuildEngine2(be3);結果:
?
2. 背景:現在有一個類,該類要實現對一個int類型的數組中的每個數進行加倍、平方、立方,并輸出
傳統解決方案一:在該類中聲明多個方法,分別是加倍、平方、立方的方法
?傳統解決方案二:在該類中聲明一個萬能方法,通過傳遞不同的參數類型來區分是執行加倍還是平方或者立方操作
?方案三:聲明一個萬能方法,傳遞一個委托進來,相當于傳遞了一個業務邏輯進行,在該方法里只需要執行即可
?代碼如下:
1 public class Calculator2 {3 //解決方案三:聲明一個萬能方法,傳遞一個委托進來,相當于傳遞了一個業務邏輯進行,在該方法里只需要執行即可4 /// <summary>5 /// 萬能方法6 /// </summary>7 /// <param name="arrs">int類型的數組 </param>8 /// <param name="mydel">系統自帶的委托(也可以自定義委托),<int,int>代表:參數和返回值均為int類型</param>9 public static void MySpecMethord(int[] arrs, Func<int,int> mydel) 10 { 11 for (int i = 0; i < arrs.Length; i++) 12 { 13 arrs[i] = mydel(arrs[i]); 14 //arrs[i] = mydel.Invoke(arrs[i]); //等價于上面那句 15 Console.WriteLine(arrs[i]); 16 } 17 } 18 19 }?三種調用形式:聲明Func委托把方法賦值給它、直接把方法名傳遞進去、傳遞lambda方法。
1 Console.WriteLine("--------------------------3.將委托當做參數,進行解耦,實現插件式編程(案例二)------------------------------------");2 int[] arr = { 1, 2, 3, 4, 5 };3 //調用形式一:4 Console.WriteLine("--------------------------調用形式一------------------------------------");5 Func<int, int> mydel = t1;6 Calculator.MySpecMethord(arr, mydel);7 //調用形式二:8 Console.WriteLine("--------------------------調用形式二------------------------------------");9 Calculator.MySpecMethord(arr, t2); 10 //調用形式三: 11 Console.WriteLine("--------------------------調用形式三------------------------------------"); 12 Calculator.MySpecMethord(arr, x => x * 2); 13 /// <summary> 14 /// 加倍方法 15 /// </summary> 16 /// <param name="x"></param> 17 /// <returns></returns> 18 public static int t1(int x) 19 { 20 return x * 2; 21 } 22 /// <summary> 23 /// 乘方方法 24 /// </summary> 25 /// <param name="x"></param> 26 /// <returns></returns> 27 public static int t2(int x) 28 { 29 return x * x; 30 } 31 /// <summary> 32 /// 立方方法 33 /// </summary> 34 /// <param name="x"></param> 35 /// <returns></returns> 36 public static int t3(int x) 37 { 38 return x * x * x; 39 }? 結果:
?三. 多播委托
? 1. 含義:所有的委托的實例都有多播的功能,自定義委托和內置委托都有,可以通過+=和-=給委托增加和刪掉不同的方法。
?下面的代碼中分別介紹四個不同類別的方法賦值給普通委托和多播委托的形式。
1 public class myMultiDelegate2 {3 //自定義一個沒有參數沒有返回值的委托4 public delegate void NoReturnNoPara();5 6 /// <summary>7 /// 測試委托常規用法8 /// </summary>9 public void show1() 10 { 11 Student student = new Student(); 12 //委托的普通用法,分別調用當前類的實例方法、當前類的靜態方法、其他類的實例方法、其他類的靜態方法 13 //聲明4個委托 14 NoReturnNoPara n1 = this.DoNothing; //調用當前類的實例方法 15 NoReturnNoPara n2 = myMultiDelegate.DoNothingStatic; //調用當前類的靜態方法 16 NoReturnNoPara n3 = student.Study; 17 NoReturnNoPara n4 = Student.StudyAdvanced; 18 19 //執行 20 n1.Invoke(); 21 n2.Invoke(); 22 n3.Invoke(); 23 n4.Invoke(); 24 } 25 /// <summary> 26 /// 測試多播委托的用法 27 /// </summary> 28 public void show2() 29 { 30 //所有的委托的實例都有多播的功能,自定義委托和內置委托都有 31 NoReturnNoPara n1 = this.DoNothing; 32 //+=就是把多個方法按順序排成列表,invoke時按順序執行 33 n1 += myMultiDelegate.DoNothingStatic; 34 n1 += Student.StudyAdvanced; 35 n1 += this.DoNothing; 36 n1 += this.DoNothing; 37 n1 += this.DoNothing; 38 n1 += this.DoNothing; 39 //-=就是從這個實例上,從后往前挨個匹配,找到第一個完全吻合的移除掉,且只移除一個,找不到不異常 40 //注意,對于委托,+= 和 -= 對null是不會報錯的 41 n1 -= this.DoNothing; 42 n1.Invoke(); 43 } 44 45 46 #region 兩個供委托調用的測試方法 47 private void DoNothing() 48 { 49 Console.WriteLine("當前類中的實例方法"); 50 } 51 private static void DoNothingStatic() 52 { 53 Console.WriteLine("當前類中的靜態方法"); 54 } 55 #endregion 56 }2. 多播委托的實際使用場景。
?實際注冊業務場景:先查詢賬號是否存在(如果不存在)→向表1中插入信息→向表2中插入信息。。。。,隨著業務新需求的變更,注冊時需要向表3中插入數據,?過了一段時間,需求又變了,不需要向表2中添加數據了。
??下述代碼是一個注冊流程的類。
1 public class RegisterUtils2 {3 //傳統的解決方案,在一個方法里書寫各種業務。4 //缺點:后期需求變更了,當需要插入更多數據的時候,只能修改該方法內部邏輯,不符合開閉原則5 6 7 //下面介紹利用多播委托進行解耦插件式開發8 public delegate void myRegisterDelegate(string userName, string userPwd);9 10 public static void myRegister(myRegisterDelegate mrd, string userName, string userPwd) 11 { 12 //對密碼進行Md5加密后在進行后續操作 13 string md5userPwd = userPwd + "MD5"; //模擬Md5 14 mrd.Invoke(userName, md5userPwd); 15 } 16 /// <summary> 17 /// 查詢方法 18 /// </summary> 19 public static void checkIsRegister(string userName, string userPwd) 20 { 21 Console.WriteLine("查詢成功"); 22 } 23 /// <summary> 24 /// 向表1中插入數據 25 /// </summary> 26 public static void writeTable1(string userName, string userPwd) 27 { 28 Console.WriteLine("向表1中插入數據{0},{1}",userName,userPwd); 29 } 30 /// <summary> 31 /// 向表2中插入數據 32 /// </summary> 33 public static void writeTable2(string userName, string userPwd) 34 { 35 Console.WriteLine("向表2中插入數據{0},{1}", userName, userPwd); 36 } 37 /// <summary> 38 /// 向表3中插入數據 39 /// </summary> 40 public static void writeTable3(string userName, string userPwd) 41 { 42 Console.WriteLine("向表3中插入數據{0},{1}", userName, userPwd); 43 } 44 }調用
1 Console.WriteLine("--------------------------3.多播委托的案例(在注冊流程中) ------------------------------------"); 2 RegisterUtils.myRegisterDelegate mrd = RegisterUtils.checkIsRegister; 3 mrd += RegisterUtils.writeTable1; 4 mrd += RegisterUtils.writeTable2; 5 //需求增加了,向表3中添加數據 6 mrd += RegisterUtils.writeTable3; 7 //需求變更了,刪除向表2中添加數據 8 mrd -= RegisterUtils.writeTable2; 9 RegisterUtils.myRegister(mrd, "maru", "123");結果:
?
補充:在下一個章節:.Net進階系列(7)-委托和事件(二)中,會繼續介紹泛型委托、內置委托、委托的其他性質、委托和事件。
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的第九节:委托和事件(1)(委托的发展历史、插件式编程、多播委托)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 核酸证明过期3分钟被拒登机?东航回应
- 下一篇: 第九节:深究并行编程Parallel类中