本來應該學習泛型與委托的,但是發現C#這里還沒有系統的記錄過委托與事件,所以先打算把委托與事件補上再繼續泛型與委托的記錄。
然后呢,今天如果沒有意外的話unity方面也會記錄一些關于編輯器擴展的筆記,還有一直想學習的C#socket(套接字)的筆記。
萬事開頭難,只要開了頭,剩下的事就可以繼續下去了。
關于委托與事件,前面其實也有記錄到,但是那是關于在unity中使用委托與事件的一個小事例而已,這里主要是記錄一下C#中關于委托與事件到底是講的什么?如何使用?以及有什么坑需要注意。
我打算把這個寫成一個系列,畢竟我也是邊學邊寫,然后呢,后續也會再次寫到如何將委托與事件運用到unity中。
還有就是,還會重新再學一下C#的設計模式,基礎一定要打牢固。
OK,廢話說完,開始進入正題:
首先什么是委托?委托,delegate,是表示對具有特定參數列表和返回類型的方法的引用的類型。 在實例化委托時,你可以將其實例與任何具有兼容簽名和返回類型的方法相關聯。 你可以通過委托實例調用方法。(MSDN解釋)
MSDN給出的解釋說實話,很惡心,還繞口。
其實,在我看來,委托就是一種自定義的類型,就類似于一個class,然后我們可以通過這個類型來創建實例對象,這個實例對象,其實類似于引用類型的對象那樣,存儲的是一個“地址”,只不過這個地址指向的是函數方法,也就是說,委托實例就類似于C++中的函數指針那樣。
委托是安全封裝方法的類型,類似于C和C++中的函數指針,委托是面向對象的。類型安全的和可靠的。
總結: 委托是一個類,它定義了方法的類型,使得可以將方法當作另一個方法的參數來進行傳遞。
可能解釋的還是很亂,不過沒關系,有個了解,不需要死扣定義。
繼續,來看一下委托的定義:public delegate void MyDelegate(string str);
注意,從這兒定義來看,其實委托的定義與一般方法的定義,只是多了一個delegate關鍵字而已。
解釋一下整個定義的意思:
public就是訪問權限,沒有什么差別。
delegate 聲明委托的關鍵字。
void,方法的返回值,如果你將某個方法綁定到委托上,那么該方法的返回值要與委托定義時的一致,在這里了都要是void。
MyDelegate,委托的名字,其實就是一個類名,我們可以使用它來創建我們的委托對象。(string str),方法參數,如果你將某個方法綁定到委托上,那么該方法的參數要與委托定義時的一致,在這里參數是一個string類型。
當我們聲明一個委托后,在編譯時,會自動生成一個委托類型,自動繼承MulticastDelegate,而MulticastDelegate又是繼承自Delegate類的。
注意:這里,我們聲明的是一個委托,雖然看起來像是聲明了一個方法,它沒有具體的實現,因為它相當于一個類。
再次繼續,如何使用委托?(先不要著急事件)
直接來看例子好了:
首先我在一個類中寫了兩個方法,一個實例方法,一個靜態方法:
public class TestMethod{
public static void StaticMethod(string str)
{
Console.WriteLine("StaticMethod:"+str);
}
public void InstantMethod(String str)
{
Console.WriteLine("InstantMethod:"+str);
} }
然后在program類中使用這個類:
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);
}
}
}
結果會是什么?輸出:OK,用這個例子來解釋一下,如何使用委托:
首先在Main()函數中,我們創建了一個委托對象 myDel,以及一個TestMethod類的對象 testMethod,
然后我們將testMethod的一個實例方法 InstanrMethod用“=”“賦值”給了myDel,有沒有很熟悉的樣子?
其實類似于,int i;i=1;這個樣子的賦值,委托就是方法的引用類型,其對象的值就是一個方法名。前提是,該方法與委托的定義一致。
然后又有兩個運算符,“+=”和“-=”,委托允許向其注冊方法,那么也就意味著,可以注銷方法,這就是observe模式的基礎。
對于委托可以有多個方法,我們稱之為多播。
而且從這個例子可以看出:使用委托對象可以封裝實例方法和靜態方法。
但是,注意,如果我們直接:MyDelegate myDel = new MyDelegate();
這是會報錯的,如下圖:
是不是會想到構造函數,這個問題的解決方式,我是采用了,將委托的定義在封裝一次,如下所示:
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);
}
}
}
怎么說呢,就類似于一種曲線救國的樣子,既然我不能new出來你,那么我就new出來封裝你的類。然后用這個類的對象來調用你,大概就是這個意思。
甚至,我們可以對委托進行“加減運算”以及判斷是否相等的邏輯判斷(PS:其實就是增加方法與刪除方法)
比如,我們來改一下Main函數里的代碼:
我們來看看結果:
看到了沒有,可以進行邏輯判斷,當然,你可以自己去嘗試一下減法的作用。
OK,關于委托的最最基礎的知識點先整理到這里,還有一些關于匿名方法和Lambda表達式運用在委托中,會在下篇中記錄。
那么現在開始記錄一下關于事件的基礎知識:
什么是事件呢?
老規矩,來看一下MSDN上的解釋:
類或對象可以通過事件向其他類或對象通知發生的相關事情,發送事件的類稱為“發行者”,接收事件的類稱為“訂戶”。
解釋的還算是清晰吧,不過可能對于一些新人來說還是一頭霧水的樣子。
那么開始事件的記錄:我先說一句:事件是對委托的隱藏。(老規矩,不要試圖去理解它,有個印象就可以了).
先來說一下,為什么需要事件?
面向對象三大特性:繼承,封裝,多態。其中封裝就是指將某些我們不想讓外部看到的東西包圍起來,給外部留出來一個接口,可以使用就行,不讓外部的人員對其進行改動,這是做使得代碼更加的安全。
我們現在都知道了,委托其實就是一個類,那么對于其對象而言,訪問權限肯定是該public的時候public,該private的時候private。
但是,當private的時候,我們就在客戶端無法調用它了,也就是說它就沒什么卵用了,那我們干嘛還聲明它出來。
但是,當public的時候,又嚴重的破壞了封裝性,我們可以隨意的修改,這點后果也很嚴重。
如果是針對別的類型,比如string,int等等,那么我們可能就會想到用屬性來對其進行封裝。
于是,Event出場了,它封裝了委托類型的變量,使得:在類的內部,不管你聲明它是public還是protected,它總是private的。在類的外部,注冊“+=”和注銷“-=”的訪問限定符與你在聲明事件時使用的訪問符相同。
OK,來看一下事件的定義:我們將委托去事件放到同一個類中,并且給一個觸發方法:
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;
這個事件的定義,其實就是比創建委托對象多了一個關鍵字event。
然后在Main函數中,我們這樣寫:
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();
這個時候,你再去用“=”就沒有卵用了。
先看一下運行結果:
當觸發事件的方法發生的時候,訂閱該事件的方法都會按照訂閱順序進行執行動作。
其實,這已經算是一個很簡版很簡版的observe模式的例子了。
還有就是,有時候我們會遇到帶有EventArgs參數的事件模型,這個是干什么用的呢?
其實這個是.Net Framework中的委托與事件,其有一個編碼規范。
我們先看一下編碼規范,下一篇再講如何編寫這樣的委托與事件。
編碼規則:
- 委托類型的名稱都應該以EventHandler結束。
- 委托的原型定義:有一個void返回值,并接受兩個輸入參數:一個Object 類型(就是監視對象),一個 EventArgs類型(或繼承自EventArgs)(其存儲了觀察者感興趣的數據,也就是事件觸發的關鍵點)。
- 事件的命名為 委托去掉 EventHandler之后剩余的部分。
- 繼承自EventArgs的類型應該以EventArgs結尾。
系列一就先記錄到這里。