浅谈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)型
?
用匿名函數(shù)初始化委托
上面為了初始化委托要定義一個(gè)函數(shù)是不是感覺(jué)有點(diǎn)麻煩,另外被賦予委托的函數(shù)一般都是通過(guò)委托實(shí)例來(lái)調(diào)用,很少會(huì)直接調(diào)用函數(shù)本身。
在.net 2.0的時(shí)候考慮到這種情況,于是匿名函數(shù)就誕生了,由于匿名函數(shù)沒(méi)有名字所以必須要用一個(gè)委托實(shí)例來(lái)引用它,定義匿名函數(shù)就是為了初始化委托
匿名函數(shù)初始化委托的原型:
<委托類(lèi)型> <實(shí)例化名>=new <委托類(lèi)型>(delegate(<函數(shù)參數(shù)>){函數(shù)體});
當(dāng)然在.net 2.0后可以用:
<委托類(lèi)型> <實(shí)例化名>=delegate(<函數(shù)參數(shù)>){函數(shù)體};
例子:
??????? delegate?void?Func1(int?i);????????delegate?int?Func2(int?i);
????????static?Func1?t1?=new?Func1(delegate(int?i)
????????{
????????????Console.WriteLine(i);
????????});
????????static?Func2?t2;
????????static?void?Main(string[]?args)
????????{
????????????t2?=?delegate(int?j)
????????????{
????????????????return?j;
????????????};
????????????t1(2);
????????????
????????????Console.WriteLine(t2(1));
????????????
????????}
?
當(dāng)然在.net 3.0的時(shí)候又有了比匿名函數(shù)更方便的東西lambda表達(dá)式,這兒就不說(shuō)了。
泛型委托
委托也支持泛型的使用
泛型委托原型:
delegate?<T1>?<委托名><T1,T2,T3...> (T1 t1,T2 t2,T3 t3...)
例子:
delegate T2 A<T1,T2>(T1 t);//定義有兩個(gè)泛型(T1,T2)的委托,T2作為委托函數(shù)返回類(lèi)型,T1作為委托函數(shù)參數(shù)類(lèi)型
static int test(int t)
{
??????return t;
}
static void Main(string[] args)
{
??????A<int, int> a =test;//將泛型委托委托<T1,T2>實(shí)例化為<int,int>,即表示有一個(gè)int類(lèi)型參數(shù)且返回類(lèi)型是int的函數(shù),所以將test用來(lái)實(shí)例化委托
??????Console.WriteLine(a(5));//輸出5
}
委托的多播性
在上面實(shí)例化委托的時(shí)候看到:必須將一個(gè)匹配函數(shù)注冊(cè)到委托上來(lái)實(shí)例化一個(gè)委托對(duì)象,但是一個(gè)實(shí)例化委托不僅可以注冊(cè)一個(gè)函數(shù)還可以注冊(cè)多個(gè)函數(shù),注冊(cè)多個(gè)函數(shù)后,在執(zhí)行委托的時(shí)候會(huì)根據(jù)注冊(cè)函數(shù)的注冊(cè)先后順序依次執(zhí)行每一個(gè)注冊(cè)函數(shù)
函數(shù)注冊(cè)委托的原型:
<委托類(lèi)型> <實(shí)例化名>+=new <委托類(lèi)型>(<注冊(cè)函數(shù)>)
例子:CheckDelegate?_checkDelegate=new?CheckDelegate(CheckMod);//將函數(shù)CheckMod注冊(cè)到委托實(shí)例_checkDelegate上
在.net 2.0開(kāi)始可以直接將匹配的函數(shù)注冊(cè)到實(shí)例化委托:
<委托類(lèi)型> <實(shí)例化名>+=<注冊(cè)函數(shù)>
例子:CheckDelegate?_checkDelegate+=CheckMod;//將函數(shù)CheckMod注冊(cè)到委托實(shí)例_checkDelegate上
之后我們還可以注冊(cè)多個(gè)函數(shù)到委托上:
例子:_checkDelegate+=CheckPositive;//將函數(shù)CheckPositive注冊(cè)到委托實(shí)例_checkDelegate上
????????_checkDelegate();//執(zhí)行這個(gè)委托實(shí)例會(huì)先執(zhí)行CheckMod()再執(zhí)行CheckPositive()
實(shí)際上使用+=符號(hào)的時(shí)候會(huì)判斷
如果此時(shí)委托還沒(méi)有實(shí)例化(委托實(shí)例為null),它會(huì)自動(dòng)用+=右邊的函數(shù)實(shí)例化委托
如果此時(shí)委托已經(jīng)實(shí)例化,它會(huì)只把+=右邊的函數(shù)注冊(cè)到委托實(shí)例上
另外有一點(diǎn)需要注意的是,如果對(duì)注冊(cè)了函數(shù)的委托實(shí)例從新使用=號(hào)賦值,相當(dāng)于是重新實(shí)例化了委托,之前在上面注冊(cè)的函數(shù)和委托實(shí)例之間也不再產(chǎn)生任何關(guān)系,后面的例子會(huì)講到這點(diǎn)!
當(dāng)然有+=注冊(cè)函數(shù)到委托,也有-=解除注冊(cè)
例子:_checkDelegate-=new?CheckDelegate(CheckPositive);//解除CheckPositive對(duì)_checkDelegate的注冊(cè)
????????_checkDelegate-=CheckPositive;//.net 2.0開(kāi)始可以用這種方式解除注冊(cè)
?
?
另外當(dāng)在委托和事件(事件的細(xì)節(jié)將在后面介紹)上注冊(cè)了多個(gè)函數(shù)后,如果委托和事件有返回值,那么調(diào)用委托和事件時(shí),返回的將是最后一個(gè)注冊(cè)函數(shù)的返回值。如下示例代碼將做詳細(xì)解釋。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks;namespace MultiDelegatesReturn {public delegate int DelMath(int i);//定義委托類(lèi)DelMath,該委托傳入一個(gè)int類(lèi)型參數(shù),返回一個(gè)int類(lèi)型參數(shù)class Program{static DelMath dMath;//通過(guò)委托類(lèi)型DelMath定義委托實(shí)例dMathstatic event DelMath eMath;//通過(guò)委托類(lèi)型DelMath定義事件實(shí)例eMath/// <summary>/// 將傳入的參數(shù)i自加后作為函數(shù)返回值/// </summary>static int IncMath(int i){i++;Console.WriteLine("IncMath has been invoked!");return i;}/// <summary>/// 將傳入的參數(shù)i自減后作為函數(shù)返回值/// </summary>static int DecMath(int i){i--;Console.WriteLine("DecMath has been invoked!");return i;}static void Main(string[] args){int i = 10;//定義int型變量i,初始值為10dMath += IncMath;//先將IncMath函數(shù)注冊(cè)到委托實(shí)例dMathdMath += DecMath;//再將DecMath函數(shù)注冊(cè)到委托實(shí)例dMathConsole.WriteLine("dMath returned:" + dMath(i).ToString());//將int型變量10傳入委托實(shí)例dMath調(diào)用后,返回的結(jié)果是9,說(shuō)明委托實(shí)例//dMath返回的是后注冊(cè)的函數(shù)DecMath的返回值eMath += IncMath;//先將IncMath函數(shù)注冊(cè)到事件實(shí)例eMatheMath += DecMath;//再將DecMath函數(shù)注冊(cè)到事件實(shí)例eMathConsole.WriteLine("eMath returned:" + eMath(i).ToString());//將int型變量10傳入事件實(shí)例eMath調(diào)用后,返回的結(jié)果也是9,說(shuō)明事件實(shí)例//eMath返回的也是后注冊(cè)的函數(shù)DecMath的返回值}} }?
?
?
c#事件
了解委托之后,就可以來(lái)談?wù)勈录?#xff0c;C#事件是什么?
c#事件的定義和委托的聲明是如此的相似:
event <委托類(lèi)型> 事件名
例子:public event CheckDelegate checkEvent;
上面的例子聲明了個(gè)事件叫checkEvent你會(huì)發(fā)現(xiàn)它只比聲明委托實(shí)例前多了個(gè)關(guān)鍵字event
聲明了事件后就可以實(shí)例化事件,注冊(cè)函數(shù)到事件,解除事件函數(shù)注冊(cè)其方法和委托的步驟如出一轍:
例子:checkEvent+=new?CheckDelegate(CheckMod);//將函數(shù)CheckMod注冊(cè)到事件checkEvent上
?????? checkEvent+=CheckMod;//.net 2.0開(kāi)始支持這種方法
???????checkEvent-=new?CheckDelegate(CheckMod);//將函數(shù)CheckMod解除對(duì)事件checkEvent的注冊(cè)
?????? checkEvent-=CheckMod;//.net 2.0開(kāi)始支持這種方法
從種種跡象都可以看出事件和委托實(shí)例是那么的相似,那么為什么不直接用委托還要用到事件呢?其實(shí)事件就是對(duì)委托的封裝,就如同c#類(lèi)中屬性對(duì)字段的封裝一樣,其封裝后可以在委托上封裝更復(fù)雜的邏輯,下面我們來(lái)看c#中事件的兩種聲明方式,來(lái)了解事件對(duì)委托的封裝
隱式聲明事件
這種方式聲明事件很簡(jiǎn)單,就如同聲明委托實(shí)例一樣:
event <委托類(lèi)型> 事件名;
例子:public event CheckDelegate checkEvent;
我們用反射機(jī)制來(lái)看看這樣聲明的事件里面裝的到底是什么東西
我們可以看到在事件被編譯后自動(dòng)生成了個(gè)private的委托實(shí)例checkEvent和兩個(gè)函數(shù)add_checkEvent和remove_checkEvent,這兩個(gè)函數(shù)分別對(duì)應(yīng)事件的+=/-=操作,另外可以看到在聲明了事件后的確是產(chǎn)生了一個(gè)和事件同名私有的委托實(shí)例checkEvent,對(duì)事件的+=/-=操作都會(huì)反映在這個(gè)同名委托實(shí)例checkEvent上,所以可以在定義事件的類(lèi)里面直接調(diào)用checkEvent()來(lái)執(zhí)行注冊(cè)函數(shù)和對(duì)checkEvent使用=號(hào)重新賦值,實(shí)際上這里操作的并不是checkEvent事件,而操作的是同名委托實(shí)例checkEvent,因此隱式聲明的事件,其實(shí)就是由一個(gè)委托實(shí)例和兩個(gè)函數(shù)封裝而成,所有的操作最終都反映在委托實(shí)例上。
(這里我補(bǔ)充下我的個(gè)人理解:事實(shí)上在一個(gè)類(lèi)的內(nèi)部是無(wú)法定義一個(gè)事件后又定義一個(gè)和事件同名的委托實(shí)例的,如果你在本例中嘗試再定義CheckDelegate checkEvent,編譯的時(shí)候會(huì)報(bào)錯(cuò)并提示已經(jīng)定義了名叫checkEvent的委托,原因是因?yàn)槭录緛?lái)就是一種特殊的委托實(shí)例(不管是隱式或顯式聲明的事件都是這樣),因此定義和事件同名的委托實(shí)例會(huì)報(bào)錯(cuò),所以我個(gè)人認(rèn)為.net在編譯的時(shí)候會(huì)把隱式聲明的事件編譯成為委托實(shí)例(和事件同名),本例中的checkEvent事件在編譯后也不再是事件轉(zhuǎn)而被編譯成了checkEvent委托實(shí)例,否則又怎么可能在定義事件的類(lèi)的內(nèi)部可以執(zhí)行事件和對(duì)事件賦值呢(這里大家可以看看我給的顯式聲明事件的例子,那里面有說(shuō)到),唯一的解釋就是隱式聲明的事件其實(shí)就是委托實(shí)例)
顯式聲明事件
其實(shí)顯示聲明事件就是要自己來(lái)手動(dòng)實(shí)現(xiàn)隱式聲明事件的一個(gè)委托實(shí)例
和兩個(gè)函數(shù):
event <委托類(lèi)型> 事件名
{
??????add
??????{
????????????//將函數(shù)注冊(cè)到自己定義的委托實(shí)例
??????}
??????remove
??????{
????????????//解除函數(shù)對(duì)自己定義的委托實(shí)例的注冊(cè)
??????}
}
例子:private CheckDelegate _checkDelete;
??????? public event CheckDelegate checkEvent
??????? {
??????????? add
??????????? {
??????????????? _checkDelete = Delegate.Combine(_checkDelete, value) as CheckDelegate;
??????????? }
??????????? remove
??????????? {
??????????????? _checkDelete = Delegate.Remove(_checkDelete, value) as CheckDelegate;
??????????? }
??????? }
//Delegate.Combine和Delegate.Remove是.net庫(kù)函數(shù),作用是合并委托實(shí)例注冊(cè)函數(shù)和移除委托實(shí)例注冊(cè)函數(shù)并返回合并和移除后的委托實(shí)例,具體解釋請(qǐng)查閱MSDN
我們?cè)儆梅瓷錂C(jī)制查看顯式聲明事件編譯后的代碼
可以看到顯示聲明事件的代碼編譯后和隱式聲明事件的代碼幾乎相同,只不過(guò)這里我們自己定義了事件操作委托實(shí)例_checkDelete。另外顯式聲明的事件不支持直接調(diào)用,就算在定義事件的類(lèi)里面也不能直接調(diào)用顯式聲明的事件(checkEvent();//這樣會(huì)報(bào)錯(cuò)),應(yīng)該調(diào)用事件委托實(shí)例(_checkDelete();)。
本文例子
俗話(huà)說(shuō)得好說(shuō)得多不如做得多,現(xiàn)在就把例子發(fā)出來(lái),例子中還講了些東西,可以執(zhí)行例子看了輸出結(jié)果后再體會(huì):
首先是個(gè)c#類(lèi)庫(kù)項(xiàng)目ClassLibrary,里面包含兩個(gè)類(lèi)分別是顯式聲明和隱式聲明事件
AutoCheckClass.cs
using?System.Collections.Generic;
using?System.Text;
namespace?ClassLibrary
{
????public?class?AutoCheckClass
????{
????????public?delegate?void?CheckDelegate(int?number);
????????public?event?CheckDelegate?checkEvent;
????????public?void?WriteInner(int?n)
????????{
????????????Console.WriteLine(n.ToString());
????????}
????????public?void?InitEvent()
????????{
????????????checkEvent?=?WriteInner;//對(duì)事件從新賦值
????????????//checkEvent?=?new?CheckDelegate(WriteInner);//也可以用委托對(duì)事件進(jìn)行賦值
????????}
????????public?void?Exec(int?n)
????????{
????????????checkEvent(n);
????????}
????????/*
?????????采用這種方式,public?event?CheckDelegate?checkEvent;會(huì)自動(dòng)生成一個(gè)private?CheckDelegate?checkEvent,
?????????對(duì)于public?event?CheckDelegate?checkEvent;的+/-操作都會(huì)在編譯時(shí)反應(yīng)在private?CheckDelegate?checkEvent上
?????????而且add/remove?.net在編譯的時(shí)候會(huì)自動(dòng)生成,不用自己再操心,缺點(diǎn)是每個(gè)事件的委托都被封裝,無(wú)法操作其內(nèi)部的委托
?????????
?????????此外采用這種方式定義的事件,可以在定義事件的類(lèi)的內(nèi)部直接對(duì)事件進(jìn)行賦值,例如可以在Exec函數(shù)中加上下面這句代碼:
?????????checkEvent?=?Exec;
?????????表示該事件可以被匹配的函數(shù)或委托賦值初始化。
?????????并且對(duì)事件進(jìn)行賦值操作,相當(dāng)于從新初始化事件內(nèi)部的委托(同名委托實(shí)例),會(huì)讓賦值之前對(duì)事件注冊(cè)的函數(shù)都不再與事件產(chǎn)生關(guān)系,具體示例請(qǐng)見(jiàn)本類(lèi)中InitEvent函數(shù)的使用效果。
?????????*/
????}
}
CheckClass.cs
using?System.Collections.Generic;
using?System.Text;
namespace?ClassLibrary
{
????public?class?CheckClass
????{
????????public?delegate?void?CheckDelegate(int?number);
????????private?CheckDelegate?_checkDelete;
????????public?event?CheckDelegate?checkEvent
????????{
????????????add
????????????{
????????????????_checkDelete?=?Delegate.Combine(_checkDelete,?value)?as?CheckDelegate;
????????????}
????????????remove
????????????{
????????????????_checkDelete?=?Delegate.Remove(_checkDelete,?value)?as?CheckDelegate;
????????????}
????????}
????????public?void?Exec(int?n)
????????{
????????????_checkDelete(n);
????????????//checkEvent?=?Exec;注意顯示定義事件的方式,不支持對(duì)事件直接進(jìn)行賦值
????????}
????????/*
?????????delegate在編譯的時(shí)候會(huì)被net編譯成一個(gè)類(lèi),如下:
?????????public?delegate?void?CheckDelegate(int?number);在編譯的時(shí)候會(huì)編譯為下面的類(lèi)
?????????public?sealed?class?CheckDelegate:System.MulticastDelegate
?????????{
????????????public?GreetingDelegate(object?@object,?IntPtr?method);
????????????public?virtual?IAsyncResult?BeginInvoke(string?name,?AsyncCallback?callback,?object?@object);
????????????public?virtual?void?EndInvoke(IAsyncResult?result);
????????????public?virtual?void?Invoke(string?name);
?????????}
?????????而System.MulticastDelegate繼承于System.Delegate,所以下面的代碼才會(huì)順利執(zhí)行
?????????_checkDelete?=?Delegate.Combine(_checkDelete,?value)?as?CheckDelegate;
?????????_checkDelete?=?Delegate.Remove(_checkDelete,?value)?as?CheckDelegate;?
?????????采用這種方法可以讓你自己指定事件的委托,甚至可以讓多個(gè)事件使用同一個(gè)委托,且自己實(shí)現(xiàn)add/remove,可以實(shí)現(xiàn)更復(fù)雜的邏輯
?????????
?????????此外需要注意的是,采用這種方式定義的事件,就算在定義事件的類(lèi)的內(nèi)部都無(wú)法對(duì)事件直接進(jìn)行賦值,例如先前在另外種定義方式說(shuō)到的在Exec函數(shù)中加上:
?????????checkEvent?=?Exec;
?????????會(huì)報(bào)錯(cuò):事件“ClassLibrary.CheckClass.checkEvent”只能出現(xiàn)在?+=?或?-=?的左邊
?????????所以在這里我們不應(yīng)該操作checkEvent,因?yàn)樗鼪](méi)有同名委托實(shí)例,而因該操作_checkDelete
?????????*/
????}
}
然后是個(gè)控制臺(tái)項(xiàng)目,需要引入上面的類(lèi)庫(kù)的dll文件
Program.cs
using?System.Collections.Generic;
using?System.Text;
using?ClassLibrary;
namespace?DeleGate
{
????class?Temp//定義此類(lèi)是為了在代碼中展示函數(shù)對(duì)委托和事件的另外一種注冊(cè)方式
????{
????????public?delegate?void?TempDelegate(int?u);
????????public?static?TempDelegate?td;
????????public?static?event?TempDelegate?ed;
????}
????class?Program
????{
????????private?static?void?CheckMod(int?number)
????????{
????????????if?(number?%?2?==?0)
????????????????Console.WriteLine("輸入的是偶數(shù)");
????????????else
????????????????Console.WriteLine("輸入的不是偶數(shù)");
????????}
????????private?static?void?CheckPositive(int?number)
????????{
????????????if?(number?>?0)
????????????????Console.WriteLine("輸入的是正數(shù)");
????????????else
????????????????Console.WriteLine("輸入的不是正數(shù)");
????????}
????????
????????static?void?Main(string[]?args)
????????{
????????????CheckClass?cc?=?new?CheckClass();
????????????cc.checkEvent?+=?new?CheckClass.CheckDelegate(CheckMod);
????????????cc.checkEvent?+=?new?CheckClass.CheckDelegate(CheckPositive);
????????????AutoCheckClass?acc?=?new?AutoCheckClass();
????????????acc.checkEvent?+=?new?AutoCheckClass.CheckDelegate(CheckMod);
????????????acc.checkEvent?+=?new?AutoCheckClass.CheckDelegate(CheckPositive);
????????????//acc.InitEvent();//執(zhí)行了這個(gè)方法后,由于對(duì)事件從新賦了值,上面對(duì)事件注冊(cè)的兩個(gè)函數(shù)都會(huì)失效
????????????
????????????Temp.td?=?CheckMod;//這表示對(duì)委托進(jìn)行賦值(等同于:Temp.td?=?new?Temp.TempDelegate(CheckMod);),和對(duì)事件賦值一樣,對(duì)委托進(jìn)行賦值相當(dāng)于初始化委托,會(huì)讓賦值之前在委托上注冊(cè)的函數(shù)與委托失去注冊(cè)關(guān)系。
????????????Temp.td?+=?CheckPositive;
????????????Console.WriteLine("Temp的結(jié)果");
????????????Temp.td(50);
????????????Temp.ed?+=?CheckMod;
????????????Temp.ed?+=?CheckPositive;
????????????Console.WriteLine("cc的結(jié)果");
????????????cc.Exec(50);
????????????Console.WriteLine("acc的結(jié)果");
????????????acc.Exec(50);
????????????
????????????Console.ReadKey();
????????}
????}
}
?
?
附加更新補(bǔ)充
調(diào)用委托實(shí)例的對(duì)象并不是調(diào)用委托函數(shù)的對(duì)象
通過(guò)前面的例子,我們了解到了,委托其實(shí)就是C#中的函數(shù)指針,有了委托我們可以像使用變量一樣來(lái)使用函數(shù)。但是請(qǐng)切記調(diào)用委托實(shí)例的對(duì)象,絕不是調(diào)用委托函數(shù)的對(duì)象。這一點(diǎn)我們通過(guò)如下例子來(lái)說(shuō)明.
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks;namespace DelagateInstanceCall {//DelegateContainer是定義委托類(lèi)型DelMethod和委托實(shí)例delMethod的類(lèi)class DelegateContainer{public delegate void DelMethod();//定義一個(gè)無(wú)參數(shù)且無(wú)返回值的委托類(lèi)型DelMethodpublic DelMethod delMethod;//定義委托類(lèi)型DelMethod的委托實(shí)例delMethodpublic int i = 100;//定義一個(gè)int類(lèi)型的變量i在類(lèi)DelegateContainer之中,賦值100}//MethodDemo是定義委托函數(shù)DisplayMethod的類(lèi)class MethodDemo{protected int i = 200;//定義一個(gè)int類(lèi)型的變量i在類(lèi)MethodDemo之中,賦值200//定義委托函數(shù)DisplayMethodpublic void DisplayMethod(){Console.WriteLine("Varible i is : " + this.i.ToString());//顯示變量i的值,通過(guò)這里的值就可以知道委托函數(shù)DisplayMethod的調(diào)用對(duì)象是誰(shuí)}}class Program{static void Main(string[] args){DelegateContainer delCon = new DelegateContainer();//構(gòu)造類(lèi)DelegateContainer的對(duì)象delConMethodDemo metDemo = new MethodDemo();//構(gòu)造類(lèi)MethodDemo的對(duì)象metDemodelCon.delMethod += metDemo.DisplayMethod;//將函數(shù)DisplayMethod注冊(cè)到委托實(shí)例delMethod,讓其作為delMethod的委托函數(shù)delCon.delMethod();//調(diào)用委托實(shí)例delMethod的時(shí)候,就會(huì)調(diào)用在它上注冊(cè)的委托函數(shù)DisplayMethod,那么在執(zhí)行委托函數(shù)DisplayMethod時(shí),其內(nèi)部代碼中的this,到底指的是//委托實(shí)例delMethod的調(diào)用對(duì)象delCon呢,還是委托函數(shù)DisplayMethod的調(diào)用對(duì)象metDemo呢?//我可以看到這里輸出的結(jié)果是"Varible i is : 200",說(shuō)明DisplayMethod內(nèi)部的this指的是委托函數(shù)DisplayMethod本身的調(diào)用對(duì)象metDemo。這里大家很容易搞混淆,由于我們上面是通過(guò)//調(diào)用委托實(shí)例delCon.delMethod來(lái)調(diào)用委托函數(shù)metDemo.DisplayMethod的,看到delCon.delMethod()時(shí)大家潛意識(shí)可能就會(huì)認(rèn)為由于調(diào)用委托實(shí)例delMethod的對(duì)象是delCon,就認(rèn)為//調(diào)用委托實(shí)例delMethod上注冊(cè)函數(shù)DisplayMethod的對(duì)象也是delCon,其實(shí)這是錯(cuò)誤的。大家一定要記住委托實(shí)例只是一個(gè)殼子,它只是用來(lái)代表在其上注冊(cè)的函數(shù),但它并不會(huì)改變注冊(cè)函數(shù)//的環(huán)境變量(比如函數(shù)的調(diào)用對(duì)象等),由于我們上面將委托函數(shù)DisplayMethod注冊(cè)到委托實(shí)例delMethod時(shí),使用的是delCon.delMethod += metDemo.DisplayMethod,所以函數(shù)的調(diào)用//對(duì)象始終都是等號(hào)右邊的對(duì)象metDemo,而不會(huì)是左邊的對(duì)象delCon,而調(diào)用等號(hào)左邊的委托實(shí)例delCon.delMethod()時(shí),相當(dāng)于就是在執(zhí)行等號(hào)右邊的metDemo.DisplayMethod(),//所以委托函數(shù)DisplayMethod的調(diào)用對(duì)象始終是metDemo。//由此請(qǐng)大家一定要記住,調(diào)用委托實(shí)例的對(duì)象和調(diào)用委托函數(shù)的對(duì)象沒(méi)有絲毫關(guān)系,要看委托函數(shù)是誰(shuí)調(diào)用的,還得要看函數(shù)注冊(cè)到委托實(shí)例時(shí),等號(hào)右邊注冊(cè)函數(shù)前的調(diào)用對(duì)象是誰(shuí)。Console.ReadKey();}} }從上面這個(gè)例子,我們可以牢牢記住,調(diào)用委托實(shí)例的對(duì)象和調(diào)用委托函數(shù)的對(duì)象沒(méi)有絲毫關(guān)系,要看委托函數(shù)是誰(shuí)調(diào)用的,還得要看函數(shù)注冊(cè)到委托實(shí)例時(shí),等號(hào)右邊注冊(cè)函數(shù)前的調(diào)用對(duì)象是誰(shuí)。這樣在使用委托時(shí)就不會(huì)出錯(cuò)和弄混淆。
轉(zhuǎn)載于:https://www.cnblogs.com/yzl050819/p/6236810.html
總結(jié)
- 上一篇: 计算程序执行时间的函数
- 下一篇: C#通过COM组件操作IE浏览器(四):