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