C#中的委托和事件詳解
從大學(xué)就開始做C#這塊,也做C#幾年了,最近又從ios轉(zhuǎn)回.Net,繼續(xù)做C#,之前也沒有寫博客的習(xí)慣,寫博客也是從我做ios的時(shí)候開始的,現(xiàn)在既然又做回了.net,那就寫點(diǎn)關(guān)于.Net的博客,可能在大牛眼里這些都是簡單基礎(chǔ)的,不過回過頭看我當(dāng)時(shí)初學(xué)的時(shí)候覺得委托事件是不容易理解的,我這里也是想著聯(lián)系著OC,兩者有比較的學(xué)習(xí)下。畢竟都是面向?qū)ο笳Z言,思想是相通的。
委托在OC中類似block,都是指向一個(gè)函數(shù),其實(shí)他沒和C++的函數(shù)指針類似。但委托還是和函數(shù)指針不太一樣,委托是完全面向?qū)ο蟮模穷愋桶踩煽康?。C++的指針僅僅指向成員函數(shù),而委托同時(shí)封裝了一個(gè)對(duì)象實(shí)例和方法。
委托聲明用于定義一個(gè)從System.Delegate類派生的類。
格式:屬性集 修飾符 delegate 返回值類型(A) 標(biāo)識(shí)符(C)(形參列表(B));
一、委托是什么?
看上面的紅字我們可以明白其實(shí)委托是一個(gè)類。其實(shí)類是什么?類也是一種數(shù)據(jù)類型,它了String類一樣,也是一個(gè)數(shù)據(jù)類型,所以呢委托其實(shí)也是一個(gè)數(shù)據(jù)類型,只是這個(gè)數(shù)據(jù)類型和其他的有點(diǎn)不同,它這個(gè)數(shù)據(jù)類型指向的是一個(gè)函數(shù)。一個(gè)返回值為A,形參列表為B的名為標(biāo)識(shí)符C的函數(shù)。其實(shí)這和OC中的block類似,block中也是用來定義函數(shù)。我們用typedef void(^myblock1)(int a,int b);來定義一個(gè)block,其實(shí)就是定義一個(gè)數(shù)據(jù)類型。上面的委托聲明也是定義了一個(gè)引用類型的數(shù)據(jù)類型。
二、委托怎么用?
上面也說了,聲明一個(gè)委托其實(shí)就是聲明了一個(gè)數(shù)據(jù)類型,和Person、String一樣都是一個(gè)數(shù)據(jù)類型。我們?cè)谑褂梦泻褪褂肞erson、String類型的數(shù)據(jù)一樣。也是先聲明:public 類型(Person、String) 變量(或?qū)傩?名。所以我們?cè)谑褂梦袝r(shí)也是這樣。只是這個(gè)變量或?qū)傩詫?duì)應(yīng)的是一個(gè)函數(shù)。
三、例子
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Delegate { //定義了一個(gè)從System.Delegate類派生的類 //也可以理解為一種數(shù)據(jù)類型 這種數(shù)據(jù)類型指向返回值為void 參數(shù)為Person對(duì)象的函數(shù) //我們也可以把Person類理解為一種數(shù)據(jù)類型 只是它包含的是Name和Age public delegate void EatFood(Person p); public class Person { public string Name { get; set; } public int Age { get; set; } public Person(string name, int age) { Name = name; Age = age; } //既然委托是一數(shù)據(jù)類型和String一樣,所以可以像聲明String對(duì)象一樣聲明代理變量 public EatFood eatFood; public void eating() { if (eatFood != null) { eatFood(this); } } } }
上面定義了一個(gè)Person類,也定義了一個(gè)定義了一個(gè)從System.Delegate類派生的類EatFood,同時(shí)在Person類中聲明了EatFood類類型的一個(gè)變量,在eating()函數(shù)中使用了這個(gè)變量。ps:請(qǐng)留意上面代碼中的注釋。下面的代碼中我們定義了兩個(gè)Person對(duì)象,一個(gè)chinesePerson一個(gè)englishPerson,而分別為兩個(gè)類的eatFood變量指定不同的函數(shù)。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Delegate { class Program { static void Main(string[] args) { Person chinesePerson = new Person("小明",25); //通過構(gòu)造函數(shù)實(shí)例化對(duì)象 chinesePerson.eatFood = new EatFood(chineseEat); chinesePerson.eating(); Console.WriteLine("--------------------------------------"); Person englishPerson = new Person("Ivan",25); //通過直接復(fù)制來實(shí)例化對(duì)象 englishPerson.eatFood = englishEat; englishPerson.eating(); Console.ReadLine(); } static void chineseEat(Person p) { Console.WriteLine("我是{0},我今年{1}歲了,我吃饅頭",p.Name,p.Age); } static void englishEat(Person p) { Console.WriteLine("I'm {0},I am {1} , I eat MianBao",p.Name,p.Age); } } }
可以看到針對(duì)不同的對(duì)象指定不同的eatFood變量則執(zhí)行的結(jié)果也不一樣。
四、委托和其他數(shù)據(jù)類型的區(qū)別
上面也說了可以把委托當(dāng)做是一個(gè)數(shù)據(jù)類型,但它和普通的數(shù)據(jù)類型還是有區(qū)別的。這可能就是現(xiàn)在的個(gè)性吧,委托也有委托的個(gè)性。
委托實(shí)例化用于創(chuàng)建委托實(shí)例,和類實(shí)例創(chuàng)建語法相同。但委托可以封裝多個(gè)方法,這些方法的集合合稱為調(diào)用列表。委托使用+、+=、-、-=運(yùn)算符向調(diào)用列表中增加或刪除方法。
我們對(duì)上面的代碼稍作改動(dòng),Person類不用改,只改Main方法中的。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Delegate { class Program { static void Main(string[] args) { Person chinesePerson = new Person("小明",25); //通過構(gòu)造函數(shù)實(shí)例化對(duì)象 chinesePerson.eatFood = new EatFood(chineseEat); chinesePerson.eatFood += englishEat; chinesePerson.eating(); Console.WriteLine("--------------------------------------"); Person englishPerson = new Person("Ivan",25); //通過直接復(fù)制來實(shí)例化對(duì)象 englishPerson.eatFood = englishEat; englishPerson.eatFood += chineseEat; englishPerson.eating(); Console.ReadLine(); } static void chineseEat(Person p) { Console.WriteLine("我是{0},我今年{1}歲了,我吃饅頭",p.Name,p.Age); } static void englishEat(Person p) { Console.WriteLine("I'm {0},I am {1} , I eat MianBao",p.Name,p.Age); } } }
為了促進(jìn)中西交流,好多人去留學(xué)也有好多來到中國的,所以在吃的方面彼此都會(huì)吃對(duì)方的。所以要增加方法來表示吃不同的食物。有了委托可以通過+=、-=來實(shí)現(xiàn)增加、刪除調(diào)用列表,這樣就方面很多。從下面的輸出結(jié)果能看到,每個(gè)Person對(duì)象都調(diào)用了chineseEat、englishEat函數(shù)。
五、好處
上面的demo也展示了委托的使用方法,通過上面的使用我們可以思考下使用它的好處。我們?nèi)绻皇褂梦衼韺?shí)現(xiàn)這個(gè)功能的話,我們可能會(huì)在Person類中做一個(gè)判斷,判斷下是Chinses還是English,可是這樣的話,如果哪天有了日本、法國等,那又要多好多個(gè)判斷??蓴U(kuò)展性不好??赡苡械臅?huì)說可以在Person里面定義一個(gè)虛方法,分別聲明Chinese、English類繼承Person類重寫虛方法,這確實(shí)是一個(gè)方法,如果有新的要擴(kuò)展的話可以直接創(chuàng)建一個(gè)新的類重寫虛方法就搞定了,不過這樣的話如果只是這個(gè)方法不同,就要寫一個(gè)類,這樣未免殺雞用牛刀了。所以說委托還是一個(gè)不錯(cuò)的選擇。如果不僅要增加語言還要增加方法那這就更麻煩了。有了委托這些全解決。
六、事件
對(duì)象之間的交互是通過消息傳遞來實(shí)現(xiàn)的,而事件就是對(duì)象發(fā)送的消息,通過發(fā)信號(hào)的形式通知操作的發(fā)生。引發(fā)事件的對(duì)象為事件發(fā)送方,捕獲事件并對(duì)其做出響應(yīng)的對(duì)象為事件接收方。在事件通信中,事件發(fā)送方不知哪個(gè)對(duì)象或方法將接收它引發(fā)的事件,所需要的是在發(fā)送方和接收方之間用一個(gè)紐帶來聯(lián)系,在C#中使用委托為這個(gè)紐帶。
事件聲明的格式:屬性集 修飾符 event 委托類型 事件名。
其實(shí)說白了就是事件是對(duì)委托變量的封裝。請(qǐng)注意上面寫的,我一直寫的是委托類型的變量,面向?qū)ο蟮娜筇卣髦痪褪欠庋b,例如變量和屬性。在上面直接使用委托來指定函數(shù),其實(shí)這和直接使用變量一樣,但是在面向?qū)ο笾幸话悴粫?huì)直接訪問變量,而是對(duì)變量進(jìn)行封裝,例如屬性{get;set;}方法。事件是對(duì)委托的封裝。我們來看一下事件的使用,和上面使用委托一樣,我們?cè)赑erson類中聲明一個(gè)事件。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Delegate { //定義了一個(gè)從System.Delegate類派生的類 //也可以理解為一種數(shù)據(jù)類型 這種數(shù)據(jù)類型指向返回值為void 參數(shù)為Person對(duì)象的函數(shù) //我們也可以把Person類理解為一種數(shù)據(jù)類型 只是它包含的是Name和Age public delegate void EatFoodDelegate(Person p); public class Person { public string Name { get; set; } public int Age { get; set; } public Person(string name, int age) { Name = name; Age = age; } //既然委托是一數(shù)據(jù)類型和String一樣,所以可以像聲明String對(duì)象一樣聲明代理變量 //public EatFood eatFood; //之前是直接聲明委托,現(xiàn)在是聲明一個(gè)事件 public event EatFoodDelegate EatFoodEventHandler; public void eating() { if (EatFoodEventHandler != null) { EatFoodEventHandler(this); } } } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Delegate { class Program { static void Main(string[] args) { Person chinesePerson = new Person("小明",25); //通過構(gòu)造函數(shù)實(shí)例化對(duì)象 chinesePerson.EatFoodEventHandler += new EatFoodDelegate(chineseEat); chinesePerson.EatFoodEventHandler += englishEat; chinesePerson.eating(); Console.WriteLine("--------------------------------------"); Person englishPerson = new Person("Ivan", 25); //在委托中 可以直接使用=來給委托對(duì)象復(fù)制 而在事件中就不能直接使用= 要使用+= englishPerson.EatFoodEventHandler += new EatFoodDelegate(englishEat); englishPerson.EatFoodEventHandler += chineseEat; englishPerson.eating(); Console.ReadLine(); } static void chineseEat(Person p) { Console.WriteLine("我是{0},我今年{1}歲了,我吃饅頭",p.Name,p.Age); } static void englishEat(Person p) { Console.WriteLine("I'm {0},I am {1} , I eat MianBao",p.Name,p.Age); } } }
上面可以看到,使用事件來實(shí)現(xiàn)了同樣的功能。
七、委托和代理設(shè)計(jì)模式的區(qū)別
不管是使用委托或者事件其實(shí)它們都是在A對(duì)象(本例中的Person對(duì)象)中調(diào)用B對(duì)象中的方法,這與設(shè)計(jì)模式中有相似之處。具體代理設(shè)計(jì)模式這里就省略了,委托和代理都是在A對(duì)象使用B對(duì)象中的方法。不過它們還是有區(qū)別的,委托中在A中直接使用的是B中的方法,是類與方法之間的,代理設(shè)計(jì)模式中是將A類中設(shè)置一個(gè)B類變量,然后通過B來使用B中的方法,是類與類之間的。這也是我的個(gè)人理解,不知道對(duì)不對(duì),錯(cuò)了的話也希望大牛指正,以免耽誤了其他的社會(huì)主義接班人。
到此這篇關(guān)于C#委托和事件的文章就介紹到這了。希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Winform開發(fā)中使用下拉列表展示字典數(shù)據(jù)的幾種方式
這篇文章介紹了Winform開發(fā)中使用下拉列表展示字典數(shù)據(jù)的幾種方式,文中通過示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-09-09C#發(fā)送HttpPost請(qǐng)求來調(diào)用WebService的方法
在C#中發(fā)送HttpPost請(qǐng)求來調(diào)用WebService中的MyAction方法,代碼如下:需要的朋友可以參考一下2013-03-03Unity實(shí)戰(zhàn)之制作動(dòng)畫編輯器
為了更方便地為UI視圖添加動(dòng)畫,將動(dòng)畫的編輯功能封裝在了UI View類中,可以通過編輯器快速的為視圖編輯動(dòng)畫。本文將通過Unity制作一個(gè)動(dòng)畫編輯器,需要的可以參考一下2022-02-02解決unity3d導(dǎo)入模型貼圖材質(zhì)丟失的問題
這篇文章主要介紹了解決unity3d導(dǎo)入模型貼圖材質(zhì)丟失的問題,具有很好的參考價(jià)值,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2021-04-04C#聯(lián)合VisionPro實(shí)現(xiàn)TCP/IP通信詳解
TCP/IP(傳輸控制協(xié)議/互聯(lián)網(wǎng)協(xié)議)是一組用于在網(wǎng)絡(luò)上進(jìn)行通信的通信協(xié)議,本文主要為大家詳細(xì)介紹了C#如何聯(lián)合VisionPro實(shí)現(xiàn)TCP/IP通信,希望對(duì)大家有所幫助2024-02-02C#實(shí)現(xiàn)運(yùn)行狀態(tài)堆疊柱狀圖
這篇文章主要為大家詳細(xì)介紹了C#實(shí)現(xiàn)運(yùn)行狀態(tài)堆疊柱狀圖,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-02-02C#實(shí)現(xiàn)控制電腦注銷,關(guān)機(jī)和重啟
這篇文章主要為大家介紹了C#如何實(shí)現(xiàn)控制電腦注銷,關(guān)機(jī)和重啟功能,本案例涉及的知識(shí)點(diǎn)包含:Process、Shell32.dll、User32.dll、Struct數(shù)據(jù)結(jié)構(gòu),感興趣的可以了解一下2022-09-09C#實(shí)現(xiàn)壓縮圖片為可控制的JPEG格式
這篇文章主要為大家詳細(xì)介紹了使用C#實(shí)現(xiàn)將圖片壓縮為質(zhì)量可自己控制的JPEG的幾種方式,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以了解一下2024-01-01