C#委托與事件初探
委托給了C#操作函數(shù)的靈活性,我們可使用委托像操作變量一樣來(lái)操作函數(shù),其實(shí)這個(gè)功能并不是C#的首創(chuàng),早在C++時(shí)代就有函數(shù)指針這一說(shuō)法,而在我看來(lái)委托就是C#的函數(shù)指針,首先先簡(jiǎn)要的介紹一下委托的基本知識(shí):
委托的定義
委托的聲明原型是
delegate <函數(shù)返回類(lèi)型> <委托名> (<函數(shù)參數(shù)>)
例子:public delegate void CheckDelegate(int number);//定義了一個(gè)委托CheckDelegate,它可以注冊(cè)返回void類(lèi)型且有一個(gè)int作為參數(shù)的函數(shù)
這樣就定義了一個(gè)委托,但是委托在.net內(nèi)相當(dāng)于聲明了一個(gè)類(lèi)(在后面的代碼中會(huì)講到確實(shí)如此),類(lèi)如果不實(shí)例化為對(duì)象,很多功能是沒(méi)有辦法使用的,委托也是如此.
委托的實(shí)例化
委托實(shí)例化的原型是
<委托類(lèi)型> <實(shí)例化名>=new <委托類(lèi)型>(<注冊(cè)函數(shù)>)
例子:CheckDelegate _checkDelegate=new CheckDelegate(CheckMod);//用函數(shù)CheckMod實(shí)例化上面的CheckDelegate 委托為_(kāi)checkDelegate
在.net 2.0開(kāi)始可以直接用匹配的函數(shù)實(shí)例化委托:
<委托類(lèi)型> <實(shí)例化名>=<注冊(cè)函數(shù)>
例子:CheckDelegate _checkDelegate=CheckMod;//用函數(shù)CheckMod實(shí)例化上面的CheckDelegate 委托為_(kāi)checkDelegate
現(xiàn)在我們就可以像使用函數(shù)一樣來(lái)使用委托了,在上面的例子中現(xiàn)在執(zhí)行_checkDelegate()就等同于執(zhí)行CheckMod(),最關(guān)鍵的是現(xiàn)在函數(shù)CheckMod相當(dāng)于放在了變量當(dāng)中,它可以傳遞給其它的CheckDelegate引用對(duì)象,而且可以作為函數(shù)參數(shù)傳遞到其他函數(shù)內(nèi),也可以作為函數(shù)的返回類(lèi)型
事件是委托的一種特殊形式,當(dāng)發(fā)生有意義的事情時(shí),事件處理對(duì)象通知過(guò)程。
一.C語(yǔ)言中的函數(shù)指針
想要理解什么是委托,就要先理解函數(shù)指針的概念。所謂函數(shù)指針,就是指向函數(shù)的指針(等于沒(méi)說(shuō)-.-)。比如我定義了兩個(gè)函數(shù)square和cube分別用于計(jì)算一個(gè)數(shù)的平方和立方,我再定義函數(shù)指針calcu,然后我讓calcu指向square,那么調(diào)用calcu時(shí)就相當(dāng)于調(diào)用了square函數(shù)(注意,此處函數(shù)指針接受的參數(shù)類(lèi)型及個(gè)數(shù)要與函數(shù)一致)。很好理解吧?不多說(shuō),上代碼。
#include <stdio.h> void square(int x) { printf("square of %d is %d\n",x,x*x); } void cube(int x) { printf("cube of %d is %d\n",x,x*x*x); } int main() { void (*calcu)(int x); calcu=square; calcu(); return ; }
二.C#中委托的實(shí)質(zhì)
委托又名委托類(lèi)型,為什么C#弄出這個(gè)東西?因?yàn)镃#是一門(mén)比較安全的語(yǔ)言,不允許操作指針,于是我們不能定義函數(shù)指針。但想要達(dá)到相同的效果,于是定義了委托類(lèi)型。所謂委托類(lèi)型,其本質(zhì)就是C中的指針類(lèi)型。于是代碼變成了這樣:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Delegate { class Program { static void square(int x) { Console.WriteLine("square of {} is {}", x, x * x); } static void cube(int x) { Console.WriteLine("cube of {} is {}", x, x * x * x); } delegate void math(int x); //定義委托類(lèi)型 static void Main(string[] args) { math calcu; calcu += square; calcu(); Console.ReadKey(); } } }
可以看出,定義委托類(lèi)型math實(shí)際上就相當(dāng)于定義了void*類(lèi)型。而委托類(lèi)型實(shí)例化得到的calcu實(shí)際上就是函數(shù)指針。(說(shuō)句題外話(huà):定義函數(shù)(方法)時(shí)要加上static是因?yàn)檎{(diào)用函數(shù)時(shí)并未實(shí)例化,只有靜態(tài)方法能夠直接通過(guò)類(lèi)調(diào)用)。
三.委托的使用方法
我們?cè)谏鲜龃a19行后面加上一行代碼 calcu+=cube; 運(yùn)行會(huì)發(fā)現(xiàn),square和cube均被調(diào)用。可以看出,符號(hào) += 表示綁定方法到委托變量,同理符號(hào) -= 表示取消綁定??梢岳斫鉃閏alcu是void **類(lèi)型,即它指向了一個(gè)數(shù)組,數(shù)組中的每一項(xiàng)都是函數(shù)指針類(lèi)型,每次調(diào)用calcu時(shí),遍歷此數(shù)組,即依次調(diào)用每個(gè)綁定的方法。
四.封裝與事件的引入
下面我們要用面向?qū)ο蟮乃枷雽⑸鲜龃a進(jìn)行封裝,使其變清晰。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Delegate { public delegate void math(int x); public class Calcu { public math calcu; } class Program { static void square(int x) { Console.WriteLine("square of {} is {}", x, x * x); } static void cube(int x) { Console.WriteLine("cube of {} is {}", x, x * x * x); } static void Main(string[] args) { Calcu c = new Calcu(); c.calcu += square; c.calcu += cube; c.calcu(); Console.ReadKey(); } } }
由于委托變量是public的,封裝的程度很低,在外部可以任意修改。為了改進(jìn)這個(gè)問(wèn)題,C#引入了事件。
所謂事件,實(shí)際上還是委托的實(shí)例化,只是其內(nèi)部多了一些定義,多了一些限制。其一,事件實(shí)際上聲明了一個(gè)private類(lèi)型的委托變量,因此在類(lèi)外無(wú)法直接調(diào)用。
于是我們將上述代碼的第12行改成這樣:
public event math calcu;
運(yùn)行之后25行報(bào)錯(cuò)了,因?yàn)閏alcu是private的,不能直接調(diào)用。但23,24行并沒(méi)有報(bào)錯(cuò)。那么問(wèn)題來(lái)了,為什么我們可以用+=來(lái)給calcu綁定方法呢?
因?yàn)槠涠?,事件還幫我們干了一件事情,就是定義了綁定方法和取消綁定方法的函數(shù),它們是public的,并且將運(yùn)算符+=,-=重載,和這兩個(gè)函數(shù)對(duì)應(yīng)。
好了,現(xiàn)在我們要寫(xiě)一個(gè)接口函數(shù)來(lái)完成計(jì)算:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Delegate { public delegate void math(int x); public class Calcu { public event math calcu; public void calculate(int x) { calcu(x); } } class Program { static void square(int x) { Console.WriteLine("square of {} is {}", x, x * x); } static void cube(int x) { Console.WriteLine("cube of {} is {}", x, x * x * x); } static void Main(string[] args) { Calcu c = new Calcu(); c.calcu += square; c.calcu += cube; c.calculate(); Console.ReadKey(); } } }
至此,基本概念已經(jīng)清晰。
想來(lái),使用事件會(huì)讓人不得不將對(duì)象封裝起來(lái),這應(yīng)該就是面向?qū)ο笏枷氲捏w現(xiàn)吧。
以上內(nèi)容是針對(duì)C#委托與事件初探的相關(guān)知識(shí),希望對(duì)大家有所幫助。
相關(guān)文章
C# 關(guān)于LoadLibrary的疑問(wèn)詳解
這篇文章主要介紹了C# 關(guān)于LoadLibrary的疑問(wèn)詳解,本篇文章通過(guò)簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-08-08C#調(diào)用dll報(bào)錯(cuò):無(wú)法加載dll,找不到指定模塊的解決
這篇文章主要介紹了C#調(diào)用dll報(bào)錯(cuò):無(wú)法加載dll,找不到指定模塊的解決問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-01-01C#特性-迭代器(上)及一些研究過(guò)程中的副產(chǎn)品
這篇文章主要介紹了C#特性-迭代器(上)及一些研究過(guò)程中的副產(chǎn)品,需要的朋友可以參考下2014-12-12C#采用mouse_event函數(shù)實(shí)現(xiàn)模擬鼠標(biāo)功能
這篇文章主要介紹了C#模擬鼠標(biāo)點(diǎn)擊小功能,通過(guò)代碼向大家做分析,需要的朋友可以參考下2015-07-07