C#中的委托和事件
一、委托
1、什么是委托
委托是面向?qū)ο蟮摹㈩?lèi)型安全的,是引用類(lèi)型。使用delegate關(guān)鍵字進(jìn)行定義。委托的本質(zhì)就是一個(gè)類(lèi),繼承自System.MulticastDelegate,而它又派生自System.Delegate。里面內(nèi)置了幾個(gè)方法 ,可以在類(lèi)的外面聲明委托,也可以在類(lèi)的內(nèi)部聲明委托。
對(duì)委托的使用:先定義,后聲明和實(shí)例化委托,然后作為參數(shù)傳遞給方法。
1.1 定義委托
下面是幾種委托定義的例子:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace MyDelegateDemo { // 也可以在類(lèi)的外面定義委托 public delegate void NoReturnNoParaOutClass(); public class MyDelegate { // 聲明無(wú)參數(shù)無(wú)返回值的泛型委托 public delegate void NoReturnNoPara<T>(T t); // 聲明無(wú)參數(shù)無(wú)返回值的委托 public delegate void NoReturnNoPara(); // 聲明有參數(shù)無(wú)返回值的委托 public delegate void NoReturnWithPara(int x, int y); // 聲明無(wú)參數(shù)有返回值的委托 public delegate int WithReturnNoPara(); // 聲明有參數(shù)有返回值的委托 public delegate string WithReturnWithPara(out int x,ref int y); } }
1.2 聲明并實(shí)例化委托
實(shí)例化委托時(shí)參數(shù)傳遞的是一個(gè)方法,方法的簽名必須和委托的簽名一樣(即方法的返回值類(lèi)型、參數(shù)列表的參數(shù)類(lèi)型都必須和定義的委托一致)。
// 委托的實(shí)例化,DoNothing是一個(gè)方法 // NoReturnNoPara是定義的無(wú)參無(wú)返回值的委托,所以DoNothing方法也必須是無(wú)參無(wú)返回值的 NoReturnNoPara method = new NoReturnNoPara(DoNothing);
DoNothing()方法定義如下:
private void DoNothing() { Console.WriteLine("This is DoNothing"); }
1.3 委托實(shí)例的調(diào)用
// 調(diào)用委托 method.Invoke(); // Invoke也可以去掉 method();
注意:委托的調(diào)用和直接執(zhí)行方法的效果是一樣的,例如:
// 調(diào)用委托 method.Invoke(); // Invoke也可以去掉 method(); // 直接執(zhí)行方法 this.DoNothing();
在控制臺(tái)的Main()方法里面,結(jié)果如下:
從截圖中能夠看出:三種方式的輸出結(jié)果都是一樣的。
2、委托類(lèi)型和委托實(shí)例
委托類(lèi)型:定義了委托實(shí)例可以調(diào)用的那類(lèi)方法,具體來(lái)說(shuō),委托類(lèi)型定義了方法的返回類(lèi)型和參數(shù)類(lèi)型,下面的代碼定義了一個(gè)委托類(lèi)型:
// 規(guī)定了可以調(diào)用的方法的返回值類(lèi)型是int,有一個(gè)類(lèi)型為int的參數(shù) delegate int Transformer(int x);
委托實(shí)例:把方法賦值給委托變量的時(shí)候就創(chuàng)建了委托實(shí)例,例如下面的代碼:
Transformer t =new Transformer(Square);
也可以簡(jiǎn)寫(xiě)為下面的形式:
Transformer t = Square;
Square是定義的一個(gè)方法,其方法定義如下:
int Square(int x) { return x*x; }
委托的實(shí)例其實(shí)就是調(diào)用者的委托:調(diào)用者調(diào)用委托,然后委托調(diào)用目標(biāo)方法,間接的把調(diào)用者和目標(biāo)方法解耦合。
講到這里可能有人會(huì)問(wèn):既然使用委托和直接調(diào)用方法的效果是一樣的,那為什么還要使用委托呢,直接調(diào)用方法多么簡(jiǎn)單?下面先來(lái)看一個(gè)實(shí)際的例子。
先定義一個(gè)Student類(lèi),里面有一些屬性和方法,Student類(lèi)定義如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace MyDelegateDemo { public class Student { public int Id { get; set; } public string Name { get; set; } public int ClassId { get; set; } public int Age { get; set; } public static void Show() { Console.WriteLine("123"); } } }
然后使用集合初始化器的方式初始化一個(gè)List<Student>集合,填充一些測(cè)試數(shù)據(jù):
private List<Student> GetStudentList() { #region 初始化數(shù)據(jù) List<Student> studentList = new List<Student>() { new Student() { Id=1, Name="老K", ClassId=2, Age=35 }, new Student() { Id=1, Name="hao", ClassId=2, Age=23 }, new Student() { Id=1, Name="大水", ClassId=2, Age=27 }, new Student() { Id=1, Name="半醉人間", ClassId=2, Age=26 }, new Student() { Id=1, Name="風(fēng)塵浪子", ClassId=2, Age=25 }, new Student() { Id=1, Name="一大鍋魚(yú)", ClassId=2, Age=24 }, new Student() { Id=1, Name="小白", ClassId=2, Age=21 }, new Student() { Id=1, Name="yoyo", ClassId=2, Age=22 }, new Student() { Id=1, Name="冰亮", ClassId=2, Age=34 }, new Student() { Id=1, Name="瀚", ClassId=2, Age=30 }, new Student() { Id=1, Name="畢帆", ClassId=2, Age=30 }, new Student() { Id=1, Name="一點(diǎn)半", ClassId=2, Age=30 }, new Student() { Id=1, Name="小石頭", ClassId=2, Age=28 }, new Student() { Id=1, Name="大海", ClassId=2, Age=30 }, new Student() { Id=3, Name="yoyo", ClassId=3, Age=30 }, new Student() { Id=4, Name="unknown", ClassId=4, Age=30 } }; #endregion return studentList; }
現(xiàn)在有一個(gè)需求,找出List<Student>集合里面年齡大于25的學(xué)生信息,代碼如下:
List<Student> studentList = this.GetStudentList(); //找出年齡大于25 List<Student> resultAge = new List<Student>();//準(zhǔn)備容器 foreach (Student student in studentList)//遍歷數(shù)據(jù)源 { if (student.Age > 25)//判斷條件 { resultAge.Add(student);//滿(mǎn)足條件的放入容器 } } Console.WriteLine($"結(jié)果一共有{resultAge.Count()}個(gè)");
使用一個(gè)foreach循環(huán)很容易得到全部年紀(jì)大于25的學(xué)生,這時(shí)又提出了需求:找出name長(zhǎng)度大于2的學(xué)生、找出Name長(zhǎng)度大于2 而且年齡大于25 而且班級(jí)id是2的學(xué)生,代碼如下:
//找出Name長(zhǎng)度大于2 List<Student> resultName = new List<Student>(); foreach (Student student in studentList) { if (student.Name.Length > 2) { resultName.Add(student); } } Console.WriteLine($"結(jié)果一共有{resultName.Count()}個(gè)"); //找出Name長(zhǎng)度大于2 而且年齡大于25 而且班級(jí)id是2 List<Student> result = new List<Student>(); foreach (Student student in studentList) { if (student.Name.Length > 2 && student.Age > 25 && student.ClassId == 2) { result.Add(student); } } Console.WriteLine($"結(jié)果一共有{result.Count()}個(gè)");
觀察上面的代碼,你會(huì)發(fā)現(xiàn)里面有很多重復(fù)的代碼:每次都要先準(zhǔn)備一個(gè)查詢(xún)結(jié)果集的集合,然后遍歷數(shù)據(jù)源,判斷條件,把滿(mǎn)足條件的放入到集合中??刹豢梢园焉厦娴拇a進(jìn)行優(yōu)化呢?請(qǐng)看下面的代碼:
private List<Student> GetList(List<Student> source, int type) { List<Student> result = new List<Student>(); foreach (Student student in source) { switch(type) { case 1: if (student.Age > 25) { result.Add(student); } break; case 2: if (student.Name.Length > 2) { result.Add(student); } break; case 3: if (student.Name.Length > 2 && student.Age > 25 && student.ClassId == 2) { result.Add(student); } break; } } return result; }
在上面這段代碼中,每次根據(jù)不同的type類(lèi)型執(zhí)行不同的判斷條件,這樣看起來(lái)可以把一些重復(fù)的代碼進(jìn)行了重用,但是這樣又會(huì)有其他的問(wèn)題:所有的判斷邏輯都寫(xiě)在了一起,如果又增加了一種type類(lèi)型或者判斷邏輯改變了,就要修改整個(gè)代碼,違反了開(kāi)閉原則。
仔細(xì)觀察上面的這段代碼:GetList()方法需要傳入一個(gè)int類(lèi)型的參數(shù),根據(jù)不同的參數(shù),執(zhí)行對(duì)應(yīng)的邏輯。那么可不可以直接傳遞邏輯進(jìn)來(lái)呢?邏輯就是方法,也就是說(shuō)能不能傳遞一個(gè)方法進(jìn)來(lái)。可能有人會(huì)問(wèn)題,方法都是進(jìn)行調(diào)用啊,怎么能進(jìn)行傳遞呢?答案是肯定的:那就是使用上面講到的委托。
以查詢(xún)年齡大于25的學(xué)生為例:邏輯就是判斷學(xué)生的年齡是否大于25,返回一個(gè)bool值,如果大于25就添加到集合中,根據(jù)邏輯,可以得到下面的方法:
private bool Than(Student student) { return student.Age > 25; }
根據(jù)這個(gè)方法的簽名可以定義如下的委托:
// 定義委托 public delegate bool ThanDelegate(Student student);
修改上面GetList的方法,把委托作為參數(shù)傳遞進(jìn)來(lái):
private List<Student> GetListDelegate(List<Student> source, ThanDelegate method) { List<Student> result = new List<Student>(); foreach (Student student in source) { // 調(diào)用委托 if (method.Invoke(student)) { result.Add(student); } } return result; }
實(shí)例化委托:
// 實(shí)例化委托 ThanDelegate method = new ThanDelegate(this.Than); List<Student> resultDele = this.GetListDelegate(studentList, method); Console.WriteLine($"結(jié)果一共有{resultDele.Count()}個(gè)");
另外兩個(gè)可以定義如下的方法:
/// <summary> /// 查詢(xún)Name長(zhǎng)度大于2 /// </summary> /// <param name="student"></param> /// <returns></returns> private bool LengthThan(Student student) { return student.Name.Length > 2; } /// <summary> /// 查詢(xún)Name長(zhǎng)度大于2 而且年齡大于25 而且班級(jí)id是2 /// </summary> /// <param name="student"></param> /// <returns></returns> private bool AllThan(Student student) { return student.Name.Length > 2 && student.Age > 25 && student.ClassId == 2; }
實(shí)例化委托如下:
//Name長(zhǎng)度大于2 ThanDelegate nameMethod = new ThanDelegate(LengthThan); List<Student> nameList= this.GetListDelegate(studentList, nameMethod); Console.WriteLine($"Name長(zhǎng)達(dá)大于2的結(jié)果一共有{nameList.Count()}個(gè)"); //Name長(zhǎng)度大于2 而且年齡大于25 而且班級(jí)id是2 ThanDelegate allMethod = new ThanDelegate(AllThan); List<Student> allList = this.GetListDelegate(studentList, allMethod); Console.WriteLine($"Name長(zhǎng)度大于2 而且年齡大于25 而且班級(jí)id是2的結(jié)果一共有{nameList.Count()}個(gè)");
觀察GetListDelegate這個(gè)方法:保留了以前公用的代碼:準(zhǔn)備一個(gè)結(jié)果集的集合、循環(huán)遍歷數(shù)據(jù)源,把符合條件的學(xué)生添加到結(jié)果集中,而判斷邏輯放到了單獨(dú)的一個(gè)方法中,如果判斷邏輯改變了或者需要增加新的判斷邏輯,只需要修改原有的判斷邏輯或者新增判斷邏輯即可,這樣可以做到不需要修改GetListDelegate()這個(gè)方法,很好的符合開(kāi)不原則。
可以總結(jié)出委托的一個(gè)應(yīng)用:委托可以解除公用邏輯(準(zhǔn)備結(jié)果集的集合、循環(huán)遍歷數(shù)據(jù)源,添加到結(jié)果集中)和具體的業(yè)務(wù)邏輯(例如判斷年齡大于25)的耦合,可以減少重復(fù)的代碼。
2、多種途徑實(shí)例化委托
委托實(shí)例化的時(shí)候不僅可以傳入當(dāng)前類(lèi)型的普通方法,還可以傳入靜態(tài)、實(shí)例方法等,例如:
// 傳入當(dāng)前類(lèi)型的普通方法 NoReturnNoPara method = new NoReturnNoPara(DoNothing); // 傳入當(dāng)前類(lèi)型的靜態(tài)方法 NoReturnNoPara methodStatic = new NoReturnNoPara(DoNothingStatic); // 傳入其他類(lèi)型的靜態(tài)方法 NoReturnNoPara methodOtherStaitc = new NoReturnNoPara(Student.StudyAdvanced); // 傳入其他類(lèi)型的普通方法 NoReturnNoPara methodOther = new NoReturnNoPara(new Student().Study);
其中DoNothingStatic()方法定義如下:
private void DoNothingStatic() { Console.WriteLine("This is DoNothingStatic"); }
Student類(lèi)的靜態(tài)方法和實(shí)例方法定義如下:
public static void StudyAdvanced() { Console.WriteLine("歡迎學(xué)習(xí)高級(jí)班課程"); } public void Study() { Console.WriteLine("學(xué)習(xí)"); }
總結(jié):實(shí)例化委托時(shí)傳入的方法只有一個(gè)要求:方法的簽名和委托的簽名一樣,即返回值類(lèi)型和參數(shù)列表一致,無(wú)論該方法來(lái)自于當(dāng)前類(lèi)型的普通方法、靜態(tài)方法或者其他類(lèi)型的實(shí)例方法和靜態(tài)方法。
3、鏈?zhǔn)轿?/h3>
鏈?zhǔn)轿幸脖环Q(chēng)為“多播委托”,其本質(zhì)是一個(gè)由多個(gè)委托組成的鏈表
。我們知道,所有的自定義委托都繼承自System.MulticastDelegate類(lèi),這個(gè)類(lèi)就是為鏈?zhǔn)轿卸O(shè)計(jì)的。當(dāng)兩個(gè)及以上的委托被鏈接到一個(gè)委托鏈時(shí),調(diào)用頭部的委托將導(dǎo)致該鏈上的所有委托方法都被執(zhí)行。
像上面實(shí)例化委托的時(shí)候,一個(gè)委托類(lèi)型的變量只能保存一個(gè)方法,使用多播委托,一個(gè)委托類(lèi)型的變量可以保存多個(gè)方法,多播委托可以增加、減少委托,Invoke的時(shí)候可以按順序執(zhí)行。
+= 為委托實(shí)例按順序增加方法,形成方法鏈,Invoke時(shí),按順序依次執(zhí)行,例如下面的代碼:
// 實(shí)例化委托 NoReturnNoPara method = new NoReturnNoPara(DoNothing); method += new NoReturnNoPara(this.DoNothing); method += new NoReturnNoPara(DoNothingStatic); method += new NoReturnNoPara(Student.StudyAdvanced); method += new NoReturnNoPara(new Student().Study); method.Invoke();
+=委托的最后輸出結(jié)果是什么是?請(qǐng)看下面的截圖:
可以看到,調(diào)用頭部的委托導(dǎo)致了所有委托方法的執(zhí)行。為委托+=增加方法讓我們看起來(lái)像是委托被修改了,其實(shí)它們并沒(méi)有被修改。事實(shí)上,委托是不變的。在給委托增加或移除方法時(shí),實(shí)際發(fā)生的是創(chuàng)建了一個(gè)新的委托。
從上面的截圖中可以看出:多播委托的執(zhí)行結(jié)果是把所有傳入的方法都執(zhí)行一遍,而且是按照實(shí)例化時(shí)傳入方法的順序依次執(zhí)行的。(上面?zhèn)魅肓藘纱萎?dāng)前類(lèi)型的DoNoThing方法,所以會(huì)執(zhí)行兩邊。)
-= 為委托實(shí)例移除方法,從方法鏈的尾部開(kāi)始匹配,遇到第一個(gè)完全吻合的,移除且只移除一個(gè),沒(méi)有也不異常。
method -= new NoReturnNoPara(this.DoNothing); method -= new NoReturnNoPara(DoNothingStatic); method -= new NoReturnNoPara(Student.StudyAdvanced); method -= new NoReturnNoPara(new Student().Study); method.Invoke();
移除委托的執(zhí)行結(jié)果是什么呢?在上面添加委托的時(shí)候傳入了5個(gè)方法,移除委托的時(shí)候移除了4個(gè)方法,應(yīng)該只會(huì)執(zhí)行DoNothing()這一個(gè)方法,是這樣的嗎?看看下面的運(yùn)行結(jié)果:
從截圖中可以看出,最后的結(jié)果和我們猜測(cè)的結(jié)果不同,除了執(zhí)行DoNothing()方法以外,還執(zhí)行了Study()方法,但是添加的時(shí)候我們只添加了一個(gè)Study()方法,而且后面又移除掉了,那為什么還會(huì)執(zhí)行這個(gè)方法呢?原因是因?yàn)樘砑雍鸵瞥龝r(shí)候不是同一個(gè)實(shí)例的Study()方法(添加和移除的時(shí)候都是new了一個(gè)新實(shí)例),所以移除的時(shí)候不會(huì)被移除掉。怎么證明上面的原因是否正確呢?請(qǐng)看下面的代碼:
Console.WriteLine("***多播委托添加方法***"); // 實(shí)例化一個(gè)Student對(duì)象 Student student = new Student(); // 實(shí)例化委托 NoReturnNoPara method = new NoReturnNoPara(DoNothing); method += new NoReturnNoPara(this.DoNothing); method += new NoReturnNoPara(DoNothingStatic); method += new NoReturnNoPara(Student.StudyAdvanced); method += new NoReturnNoPara(new Student().Study); method += new NoReturnNoPara(student.Study); method.Invoke(); Console.WriteLine("***下面是多播委托移除方法***"); //-= 為委托實(shí)例移除方法,從方法鏈的尾部開(kāi)始匹配,遇到第一個(gè)完全吻合的,移除且只移除一個(gè),沒(méi)有也不異常 method -= new NoReturnNoPara(this.DoNothing); method -= new NoReturnNoPara(DoNothingStatic); method -= new NoReturnNoPara(Student.StudyAdvanced);//不是同一個(gè)實(shí)例,所以是不同的方法 method -= new NoReturnNoPara(student.Study); method.Invoke();
查看運(yùn)行結(jié)果:
從運(yùn)行結(jié)果中可以看出上面的原因是正確的。
注意:多播委托不能異步調(diào)用(即調(diào)用BeginInvoke()),因?yàn)槎嗖ノ欣锩嬗泻芏喾椒?,異步調(diào)用的時(shí)候不知道該怎樣執(zhí)行,是把所有方法同步執(zhí)行呢還是按照順序依次執(zhí)行呢,BeginInvoke不知道該如何調(diào)用,所以多播委托不能直接調(diào)用BeginInvoke()方法。那如果我想使用該怎么辦呢?可以使用GetInvocationList()方法,F(xiàn)12查看GetInvocationList()方法的定義:
那么可以使用如下的代碼:
foreach(NoReturnNoPara item in method.GetInvocationList()) { item.Invoke(); }
上面的多播委托例子中一直都是使用的沒(méi)有返回值的委托,如果是有返回值的委托,那么返回值是什么呢?請(qǐng)看下面的例子:
先定義幾個(gè)有返回值的方法:
private int GetSomething() { return 1; } private int GetSomething2() { return 2; } private int GetSomething3() { return 3; }
實(shí)例化委托:
WithReturnNoPara methodWithReturn = new WithReturnNoPara(this.GetSomething); methodWithReturn += new WithReturnNoPara(this.GetSomething2); methodWithReturn += new WithReturnNoPara(this.GetSomething3); int iResult = methodWithReturn.Invoke(); Console.WriteLine("返回值:"+iResult.ToString());
運(yùn)行程序查看結(jié)果:
從截圖中可以看出:帶返回值的多播委托的結(jié)果是最后添加的方法的返回值。中間方法的返回值都會(huì)被丟棄。
總結(jié):多播委托一般用來(lái)調(diào)用無(wú)返回值的方法,不用來(lái)調(diào)用有返回值的方法,因?yàn)橛蟹祷刂档亩嗖ノ兄虚g的結(jié)果都會(huì)被丟棄掉。
總結(jié)
我們可以對(duì)委托做如下的總結(jié):
- 委托是不可變的。
- 使用+=或-=操作符時(shí),實(shí)際上是創(chuàng)建了新的委托實(shí)例,并把它賦給當(dāng)前的委托變量。
- 如果多播委托的返回類(lèi)型不是void,那么調(diào)用者從最后一個(gè)被調(diào)用的方法來(lái)接收返回值,前面的方法仍然會(huì)被調(diào)用,但是其返回值就被棄用了。
- 所有的委托類(lèi)型都派生于System.MulticastDelegate,而它又派生于System.Delegate。
- C#會(huì)把作用于委托的+、-、+=、-=操作編譯成使用System.Delegate的Combine和Remove兩個(gè)靜態(tài)方法。
二、事件
1、什么是事件
事件是帶event關(guān)鍵字的委托的實(shí)例。
2、如何聲明事件
// 聲明委托 public delegate void MiaoDelegate(); // 聲明事件 public event MiaoDelegate MiaoDelegateHandlerEvent;
3、委托和事件的區(qū)別和聯(lián)系
委托是一個(gè)類(lèi)型,例如Student類(lèi)。
事件是委托類(lèi)型的一個(gè)實(shí)例,例如具體的一個(gè)學(xué)生。
4、為什么要是有事件
事件不能直接執(zhí)行Invoke()方法,可以限制變量被外部調(diào)用或者直接賦值。
注意:即使是在子類(lèi)中,事件也不能調(diào)用Invoke()方法。
三、委托和事件的應(yīng)用
來(lái)看下面的一個(gè)例子:
有一個(gè)Cat類(lèi),里面有一個(gè)Miao()的方法,貓叫了一聲,然后觸發(fā)一系列的后續(xù)動(dòng)作,通常的實(shí)現(xiàn)代碼如下:
public void Miao() { Console.WriteLine("{0} Miao", this.GetType().Name); new Mouse().Run(); new Baby().Cry(); new Mother().Wispher(); new Father().Roar(); new Neighbor().Awake(); new Stealer().Hide(); new Dog().Wang(); }
調(diào)用Miao()方法:
// 實(shí)例化 Cat cat = new Cat(); cat.Miao();
上面的代碼可以實(shí)現(xiàn)上述的需求,但是這段代碼耦合性很強(qiáng),因?yàn)槭窃贛iao()方法里面直接調(diào)用別的實(shí)例的方法,以后無(wú)論是增加或者修改、調(diào)整方法的調(diào)用順序,都要修改Miao()方法,使得Miao()方法不穩(wěn)定。
下面使用委托來(lái)優(yōu)化上面的代碼:
// 聲明委托 public delegate void MiaoDelegate(); public MiaoDelegate MiaoDelegateHandler; public void MiaoNew() { Console.WriteLine("{0} MiaoNew", this.GetType().Name); if (this.MiaoDelegateHandler != null) { this.MiaoDelegateHandler.Invoke(); } }
調(diào)用:
Cat cat = new Cat(); // 多播委托 cat.MiaoDelegateHandler += new MiaoDelegate(new Mouse().Run); cat.MiaoDelegateHandler += new MiaoDelegate(new Baby().Cry); cat.MiaoDelegateHandler += new MiaoDelegate(new Mother().Wispher); cat.MiaoDelegateHandler += new MiaoDelegate(new Brother().Turn); cat.MiaoDelegateHandler += new MiaoDelegate(new Father().Roar); cat.MiaoDelegateHandler += new MiaoDelegate(new Neighbor().Awake); cat.MiaoDelegateHandler += new MiaoDelegate(new Stealer().Hide); cat.MiaoDelegateHandler += new MiaoDelegate(new Dog().Wang); cat.MiaoNew();
上面的委托也可以改為事件實(shí)現(xiàn):
// 聲明事件 public event MiaoDelegate MiaoDelegateHandlerEvent; public void MiaoNewEvent() { Console.WriteLine("{0} MiaoNewEvent", this.GetType().Name); if (this.MiaoDelegateHandlerEvent != null) { this.MiaoDelegateHandlerEvent.Invoke(); } }
調(diào)用:
Cat cat = new Cat(); cat.MiaoDelegateHandlerEvent += new MiaoDelegate(new Mouse().Run); cat.MiaoDelegateHandlerEvent += new MiaoDelegate(new Baby().Cry); cat.MiaoDelegateHandlerEvent += new MiaoDelegate(new Mother().Wispher); cat.MiaoDelegateHandlerEvent += new MiaoDelegate(new Brother().Turn); cat.MiaoDelegateHandlerEvent += new MiaoDelegate(new Father().Roar); cat.MiaoDelegateHandlerEvent += new MiaoDelegate(new Neighbor().Awake); cat.MiaoDelegateHandlerEvent += new MiaoDelegate(new Stealer().Hide); cat.MiaoDelegateHandlerEvent += new MiaoDelegate(new Dog().Wang); cat.MiaoNewEvent();
到此這篇關(guān)于C#委托和事件的文章就介紹到這了。希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
WPF利用ValueConverter實(shí)現(xiàn)值轉(zhuǎn)換器
值轉(zhuǎn)換器在WPF開(kāi)發(fā)中是非常常見(jiàn)的,值轉(zhuǎn)換器可以幫助我們很輕松地實(shí)現(xiàn),界面數(shù)據(jù)展示的問(wèn)題。本文將通過(guò)WPF?ValueConverter實(shí)現(xiàn)簡(jiǎn)單的值轉(zhuǎn)換器,希望對(duì)大家有所幫助2023-03-03c#下注冊(cè)表操作的一個(gè)小細(xì)節(jié)
c#下注冊(cè)表操作的一個(gè)小細(xì)節(jié)...2007-11-11C#中winform控制textbox輸入只能為數(shù)字的方法
這篇文章主要介紹了C#中winform控制textbox輸入只能為數(shù)字的方法,包括使用keyPress事件限制鍵盤(pán)輸入以及TextChanged事件限制粘貼等情況,來(lái)實(shí)現(xiàn)控制輸入為數(shù)字的功能,需要的朋友可以參考下2015-01-01c#重寫(xiě)TabControl控件實(shí)現(xiàn)關(guān)閉按鈕的方法
這是關(guān)于c#重寫(xiě)TabControl控件實(shí)現(xiàn)關(guān)閉按鈕的例子,整理了一下,與大家分享。2013-04-04Unity UGUI的HorizontalLayoutGroup水平布局組件介紹使用
這篇文章主要為大家介紹了Unity UGUI的HorizontalLayoutGroup水平布局組件介紹使用,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-07-07Unity調(diào)用手機(jī)攝像機(jī)識(shí)別二維碼
這篇文章主要為大家詳細(xì)介紹了Unity調(diào)用手機(jī)攝像機(jī)識(shí)別二維碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-07-07C#實(shí)現(xiàn)為一張大尺寸圖片創(chuàng)建縮略圖的方法
這篇文章主要介紹了C#實(shí)現(xiàn)為一張大尺寸圖片創(chuàng)建縮略圖的方法,涉及C#創(chuàng)建縮略圖的相關(guān)圖片操作技巧,需要的朋友可以參考下2015-06-06