本來應(yīng)該學(xué)習(xí)泛型與委托的巩步,但是發(fā)現(xiàn)C#這里還沒有系統(tǒng)的記錄過委托與事件旁赊,所以先打算把委托與事件補(bǔ)上再繼續(xù)泛型與委托的記錄。
然后呢竟闪,今天如果沒有意外的話unity方面也會(huì)記錄一些關(guān)于編輯器擴(kuò)展的筆記离福,還有一直想學(xué)習(xí)的C#socket(套接字)的筆記。
萬事開頭難瘫怜,只要開了頭术徊,剩下的事就可以繼續(xù)下去了。
關(guān)于委托與事件鲸湃,前面其實(shí)也有記錄到赠涮,但是那是關(guān)于在unity中使用委托與事件的一個(gè)小事例而已,這里主要是記錄一下C#中關(guān)于委托與事件到底是講的什么暗挑?如何使用笋除?以及有什么坑需要注意。
我打算把這個(gè)寫成一個(gè)系列炸裆,畢竟我也是邊學(xué)邊寫垃它,然后呢,后續(xù)也會(huì)再次寫到如何將委托與事件運(yùn)用到unity中烹看。
還有就是国拇,還會(huì)重新再學(xué)一下C#的設(shè)計(jì)模式,基礎(chǔ)一定要打牢固惯殊。
OK酱吝,廢話說完,開始進(jìn)入正題:
首先什么是委托土思?委托务热,delegate,是表示對(duì)具有特定參數(shù)列表和返回類型的方法的引用的類型己儒。 在實(shí)例化委托時(shí)崎岂,你可以將其實(shí)例與任何具有兼容簽名和返回類型的方法相關(guān)聯(lián)。 你可以通過委托實(shí)例調(diào)用方法闪湾。(MSDN解釋)
MSDN給出的解釋說實(shí)話冲甘,很惡心,還繞口。
其實(shí)损合,在我看來省艳,委托就是一種自定義的類型,就類似于一個(gè)class嫁审,然后我們可以通過這個(gè)類型來創(chuàng)建實(shí)例對(duì)象,這個(gè)實(shí)例對(duì)象赖晶,其實(shí)類似于引用類型的對(duì)象那樣律适,存儲(chǔ)的是一個(gè)“地址”,只不過這個(gè)地址指向的是函數(shù)方法遏插,也就是說捂贿,委托實(shí)例就類似于C++中的函數(shù)指針那樣。
委托是安全封裝方法的類型胳嘲,類似于C和C++中的函數(shù)指針厂僧,委托是面向?qū)ο蟮摹n愋桶踩暮涂煽康摹?br>
總結(jié): 委托是一個(gè)類了牛,它定義了方法的類型颜屠,使得可以將方法當(dāng)作另一個(gè)方法的參數(shù)來進(jìn)行傳遞。
可能解釋的還是很亂鹰祸,不過沒關(guān)系甫窟,有個(gè)了解,不需要死扣定義蛙婴。
繼續(xù)粗井,來看一下委托的定義:public delegate void MyDelegate(string str);
注意,從這兒定義來看街图,其實(shí)委托的定義與一般方法的定義浇衬,只是多了一個(gè)delegate關(guān)鍵字而已。
解釋一下整個(gè)定義的意思:
public就是訪問權(quán)限餐济,沒有什么差別耘擂。
delegate 聲明委托的關(guān)鍵字。
void颤介,方法的返回值梳星,如果你將某個(gè)方法綁定到委托上,那么該方法的返回值要與委托定義時(shí)的一致滚朵,在這里了都要是void冤灾。
MyDelegate,委托的名字辕近,其實(shí)就是一個(gè)類名韵吨,我們可以使用它來創(chuàng)建我們的委托對(duì)象。(string str)移宅,方法參數(shù)归粉,如果你將某個(gè)方法綁定到委托上椿疗,那么該方法的參數(shù)要與委托定義時(shí)的一致,在這里參數(shù)是一個(gè)string類型糠悼。
當(dāng)我們聲明一個(gè)委托后届榄,在編譯時(shí),會(huì)自動(dòng)生成一個(gè)委托類型倔喂,自動(dòng)繼承MulticastDelegate铝条,而MulticastDelegate又是繼承自Delegate類的。
注意:這里席噩,我們聲明的是一個(gè)委托班缰,雖然看起來像是聲明了一個(gè)方法,它沒有具體的實(shí)現(xiàn)悼枢,因?yàn)樗喈?dāng)于一個(gè)類埠忘。
再次繼續(xù),如何使用委托馒索?(先不要著急事件)
直接來看例子好了:
首先我在一個(gè)類中寫了兩個(gè)方法莹妒,一個(gè)實(shí)例方法,一個(gè)靜態(tài)方法:
public class TestMethod{
public static void StaticMethod(string str)
{
Console.WriteLine("StaticMethod:"+str);
}
public void InstantMethod(String str)
{
Console.WriteLine("InstantMethod:"+str);
} }
然后在program類中使用這個(gè)類:
class Program
{
static void Main(string[] args){
MyDelegate myDel;
TestMethod testMethod = new TestMethod();
myDel=testMethod.InstantMethod;
myDel+=TestMethod.StaticMethod;
myDel("AAA");
myDel-=testMethod.InstantMethod;
myDel("BBB");
}
public delegate void MyDelegate(string str);
public class TestMethod
{
public static void StaticMethod(string str)
{
Console.WriteLine("StaticMethod:"+str);
}
public void InstantMethod(String str)
{
Console.WriteLine("InstantMethod:"+str);
}
}
}
結(jié)果會(huì)是什么双揪?輸出:OK动羽,用這個(gè)例子來解釋一下,如何使用委托:
首先在Main()函數(shù)中渔期,我們創(chuàng)建了一個(gè)委托對(duì)象 myDel运吓,以及一個(gè)TestMethod類的對(duì)象 testMethod,
然后我們將testMethod的一個(gè)實(shí)例方法 InstanrMethod用“=”“賦值”給了myDel疯趟,有沒有很熟悉的樣子拘哨?
其實(shí)類似于,int i;i=1;這個(gè)樣子的賦值信峻,委托就是方法的引用類型倦青,其對(duì)象的值就是一個(gè)方法名。前提是盹舞,該方法與委托的定義一致产镐。
然后又有兩個(gè)運(yùn)算符,“+=”和“-=”踢步,委托允許向其注冊(cè)方法癣亚,那么也就意味著,可以注銷方法获印,這就是observe模式的基礎(chǔ)述雾。
對(duì)于委托可以有多個(gè)方法,我們稱之為多播。
而且從這個(gè)例子可以看出:使用委托對(duì)象可以封裝實(shí)例方法和靜態(tài)方法玻孟。
但是面徽,注意,如果我們直接:MyDelegate myDel = new MyDelegate()匣掸;
這是會(huì)報(bào)錯(cuò)的斗忌,如下圖:
是不是會(huì)想到構(gòu)造函數(shù),這個(gè)問題的解決方式旺聚,我是采用了,將委托的定義在封裝一次眶蕉,如下所示:
namespace class1
{
class Program
{
static void Main(string[] args)
{
Method mt = new Method();
Del del = new Del();
del.myDel = mt.InstantMethod;
del.myDel += Method.StaticMethod;
Console.ReadKey();
}
}
class Del
{
public MyDelegate myDel;
}
public delegate void MyDelegate(string str);
class Method
{
public void InstantMethod(string str)
{
Console.WriteLine("InstantMethod" + str);
}
public static void StaticMethod(string str)
{
Console.WriteLine("StaticMethod" + str);
}
}
}
怎么說呢砰粹,就類似于一種曲線救國(guó)的樣子,既然我不能new出來你造挽,那么我就new出來封裝你的類碱璃。然后用這個(gè)類的對(duì)象來調(diào)用你,大概就是這個(gè)意思饭入。
甚至嵌器,我們可以對(duì)委托進(jìn)行“加減運(yùn)算”以及判斷是否相等的邏輯判斷(PS:其實(shí)就是增加方法與刪除方法)
比如,我們來改一下Main函數(shù)里的代碼:
我們來看看結(jié)果:
看到了沒有谐丢,可以進(jìn)行邏輯判斷爽航,當(dāng)然,你可以自己去嘗試一下減法的作用乾忱。
OK讥珍,關(guān)于委托的最最基礎(chǔ)的知識(shí)點(diǎn)先整理到這里,還有一些關(guān)于匿名方法和Lambda表達(dá)式運(yùn)用在委托中窄瘟,會(huì)在下篇中記錄衷佃。
那么現(xiàn)在開始記錄一下關(guān)于事件的基礎(chǔ)知識(shí):
什么是事件呢?
老規(guī)矩蹄葱,來看一下MSDN上的解釋:
類或?qū)ο罂梢酝ㄟ^事件向其他類或?qū)ο笸ㄖl(fā)生的相關(guān)事情氏义,發(fā)送事件的類稱為“發(fā)行者”,接收事件的類稱為“訂戶”图云。
解釋的還算是清晰吧惯悠,不過可能對(duì)于一些新人來說還是一頭霧水的樣子。
那么開始事件的記錄:我先說一句:事件是對(duì)委托的隱藏琼稻。(老規(guī)矩吮螺,不要試圖去理解它,有個(gè)印象就可以了).
先來說一下,為什么需要事件鸠补?
面向?qū)ο笕筇匦裕豪^承萝风,封裝,多態(tài)紫岩。其中封裝就是指將某些我們不想讓外部看到的東西包圍起來规惰,給外部留出來一個(gè)接口,可以使用就行泉蝌,不讓外部的人員對(duì)其進(jìn)行改動(dòng)歇万,這是做使得代碼更加的安全。
我們現(xiàn)在都知道了勋陪,委托其實(shí)就是一個(gè)類贪磺,那么對(duì)于其對(duì)象而言,訪問權(quán)限肯定是該public的時(shí)候public诅愚,該private的時(shí)候private寒锚。
但是,當(dāng)private的時(shí)候违孝,我們就在客戶端無法調(diào)用它了刹前,也就是說它就沒什么卵用了,那我們干嘛還聲明它出來雌桑。
但是喇喉,當(dāng)public的時(shí)候,又嚴(yán)重的破壞了封裝性校坑,我們可以隨意的修改拣技,這點(diǎn)后果也很嚴(yán)重。
如果是針對(duì)別的類型撒踪,比如string过咬,int等等,那么我們可能就會(huì)想到用屬性來對(duì)其進(jìn)行封裝制妄。
于是掸绞,Event出場(chǎng)了,它封裝了委托類型的變量耕捞,使得:在類的內(nèi)部衔掸,不管你聲明它是public還是protected,它總是private的俺抽。在類的外部敞映,注冊(cè)“+=”和注銷“-=”的訪問限定符與你在聲明事件時(shí)使用的訪問符相同。
OK磷斧,來看一下事件的定義:我們將委托去事件放到同一個(gè)類中振愿,并且給一個(gè)觸發(fā)方法:
class EventTest
{
public delegate void MethodHandler(string str);
public event MethodHandler MethodEvent;
public void Print(string str)
{
if (MethodEvent != null)
{
MethodEvent(str);
}
}
}
看到了沒有捷犹,public event MethodHandler MethodEvent;
這個(gè)事件的定義,其實(shí)就是比創(chuàng)建委托對(duì)象多了一個(gè)關(guān)鍵字event冕末。
然后在Main函數(shù)中萍歉,我們這樣寫:
EventTest ev = new EventTest();
Method mt = new Method();
ev.MethodEvent += mt.method;
ev.MethodEvent += mt.InstantMethod;
ev.MethodEvent += Method.StaticMethod;
ev.Print("ppppppppppppppppp");
Console.ReadKey();
這個(gè)時(shí)候,你再去用“=”就沒有卵用了档桃。
先看一下運(yùn)行結(jié)果:
當(dāng)觸發(fā)事件的方法發(fā)生的時(shí)候枪孩,訂閱該事件的方法都會(huì)按照訂閱順序進(jìn)行執(zhí)行動(dòng)作。
其實(shí)藻肄,這已經(jīng)算是一個(gè)很簡(jiǎn)版很簡(jiǎn)版的observe模式的例子了蔑舞。
還有就是,有時(shí)候我們會(huì)遇到帶有EventArgs參數(shù)的事件模型嘹屯,這個(gè)是干什么用的呢攻询?
其實(shí)這個(gè)是.Net Framework中的委托與事件,其有一個(gè)編碼規(guī)范州弟。
我們先看一下編碼規(guī)范蜕窿,下一篇再講如何編寫這樣的委托與事件。
編碼規(guī)則:
- 委托類型的名稱都應(yīng)該以EventHandler結(jié)束呆馁。
- 委托的原型定義:有一個(gè)void返回值,并接受兩個(gè)輸入?yún)?shù):一個(gè)Object 類型(就是監(jiān)視對(duì)象)毁兆,一個(gè) EventArgs類型(或繼承自EventArgs)(其存儲(chǔ)了觀察者感興趣的數(shù)據(jù)浙滤,也就是事件觸發(fā)的關(guān)鍵點(diǎn))。
- 事件的命名為 委托去掉 EventHandler之后剩余的部分气堕。
- 繼承自EventArgs的類型應(yīng)該以EventArgs結(jié)尾纺腊。
系列一就先記錄到這里。