從大學就開始作C#這塊,也作C#幾年了,最近又從ios轉回.Net,繼續作C#,以前也沒有寫博客的習慣,寫博客也是從我作ios的時候開始的,如今既然又作回了.net,那就寫點關於.Net的博客,可能在大牛眼裏這些都是簡單基礎的,不過回過頭看我當時初學的時候以爲委託事件是不容易理解的,我這裏也是想着聯繫着OC,二者有比較的學習下。畢竟都是面嚮對象語言,思想是相通的。ios
委託在OC中相似block,都是指向一個函數,其實他沒和C++的函數指針相似。但委託仍是和函數指針不太同樣,委託是徹底面向對象的,是類型安全可靠的。C++的指針僅僅指向成員函數,而委託同時封裝了一個對象實例和方法。設計模式
委託聲明用於定義一個從System.Delegate類派生的類。安全
格式:屬性集 修飾符 delegate 返回值類型(A) 標識符(C)(形參列表(B));函數
1、委託是什麼?學習
看上面的紅字咱們能夠明白其實委託是一個類。其實類是什麼?類也是一種數據類型,它了String類同樣,也是一個數據類型,因此呢委託其實也是一個數據類型,只是這個數據類型和其餘的有點不一樣,它這個數據類型指向的是一個函數。一個返回值爲A,形參列表爲B的名爲標識符C的函數。其實這和OC中的block相似,block中也是用來定義函數。咱們用typedef void(^myblock1)(int a,int b);來定義一個block,其實就是定義一個數據類型。上面的委託聲明也是定義了一個引用類型的數據類型。this
2、委託怎麼用?spa
上面也說了,聲明一個委託其實就是聲明瞭一個數據類型,和Person、String同樣都是一個數據類型。咱們在使用委託和使用Person、String類型的數據同樣。也是先聲明:public 類型(Person、String) 變量(或屬性)名。因此咱們在使用委託時也是這樣。只是這個變量或屬性對應的是一個函數。.net
3、例子設計
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Delegate { //定義了一個從System.Delegate類派生的類 //也能夠理解爲一種數據類型 這種數據類型指向返回值爲void 參數爲Person對象的函數 //咱們也能夠把Person類理解爲一種數據類型 只是它包含的是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; } //既然委託是一數據類型和String同樣,因此能夠像聲明String對象同樣聲明代理變量 public EatFood eatFood; public void eating() { if (eatFood != null) { eatFood(this); } } } }
上面定義了一個Person類,也定義了一個定義了一個從System.Delegate類派生的類EatFood,同時在Person類中聲明瞭EatFood類類型的一個變量,在eating()函數中使用了這個變量。ps:請留意上面代碼中的註釋。下面的代碼中咱們定義了兩個Person對象,一個chinesePerson一個englishPerson,而分別爲兩個類的eatFood變量指定不一樣的函數。代理
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); //經過構造函數實例化對象 chinesePerson.eatFood = new EatFood(chineseEat); chinesePerson.eating(); Console.WriteLine("--------------------------------------"); Person englishPerson = new Person("Ivan",25); //經過直接複製來實例化對象 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變量則執行的結果也不同。
4、委託和其餘數據類型的區別
上面也說了能夠把委託當作是一個數據類型,但它和普通的數據類型仍是有區別的。這可能就是如今的個性吧,委託也有委託的個性。
委託實例化用於建立委託實例,和類實例建立語法相同。但委託能夠封裝多個方法,這些方法的集合合稱爲調用列表。委託使用+、+=、-、-=運算符向調用列表中增長或刪除方法。
咱們對上面的代碼稍做改動,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); //經過構造函數實例化對象 chinesePerson.eatFood = new EatFood(chineseEat); chinesePerson.eatFood += englishEat; chinesePerson.eating(); Console.WriteLine("--------------------------------------"); Person englishPerson = new Person("Ivan",25); //經過直接複製來實例化對象 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); } } }
爲了促進中西交流,好多人去留學也有好多來到中國的,因此在吃的方面彼此都會吃對方的。因此要增長方法來表示吃不一樣的食物。有了委託能夠經過+=、-=來實現增長、刪除調用列表,這樣就方面不少。從下面的輸出結果能看到,每一個Person對象都調用了chineseEat、englishEat函數。
5、好處
上面的demo也展現了委託的使用方法,經過上面的使用咱們能夠思考下使用它的好處。咱們若是不使用委託來實現這個功能的話,咱們可能會在Person類中作一個判斷,判斷下是Chinses仍是English,但是這樣的話,若是哪天有了日本、法國等,那又要多好多個判斷。可擴展性很差。可能有的會說能夠在Person裏面定義一個虛方法,分別聲明Chinese、English類繼承Person類重寫虛方法,這確實是一個方法,若是有新的要擴展的話能夠直接建立一個新的類重寫虛方法就搞定了,不過這樣的話若是隻是這個方法不一樣,就要寫一個類,這樣未免殺雞用牛刀了。因此說委託仍是一個不錯的選擇。若是不只要增長語言還要增長方法那這就更麻煩了。有了委託這些全解決。
6、事件
對象之間的交互是經過消息傳遞來實現的,而事件就是對象發送的消息,經過發信號的形式通知操做的發生。引起事件的對象爲事件發送方,捕獲事件並對其作出響應的對象爲事件接收方。在事件通訊中,事件發送方不知哪一個對象或方法將接收它引起的事件,所須要的是在發送方和接收方之間用一個紐帶來聯繫,在C#中使用委託爲這個紐帶。
事件聲明的格式:屬性集 修飾符 event 委託類型 事件名。
其實說白了就是事件是對委託變量的封裝。請注意上面寫的,我一直寫的是委託類型的變量,面向對象的三大特徵之一就是封裝,例如變量和屬性。在上面直接使用委託來指定函數,其實這和直接使用變量同樣,可是在面向對象中通常不會直接訪問變量,而是對變量進行封裝,例如屬性{get;set;}方法。事件是對委託的封裝。咱們來看一下事件的使用,和上面使用委託同樣,咱們在Person類中聲明一個事件。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Delegate { //定義了一個從System.Delegate類派生的類 //也能夠理解爲一種數據類型 這種數據類型指向返回值爲void 參數爲Person對象的函數 //咱們也能夠把Person類理解爲一種數據類型 只是它包含的是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; } //既然委託是一數據類型和String同樣,因此能夠像聲明String對象同樣聲明代理變量 //public EatFood eatFood; //以前是直接聲明委託,如今是聲明一個事件 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); //經過構造函數實例化對象 chinesePerson.EatFoodEventHandler += new EatFoodDelegate(chineseEat); chinesePerson.EatFoodEventHandler += englishEat; chinesePerson.eating(); Console.WriteLine("--------------------------------------"); Person englishPerson = new Person("Ivan", 25); //在委託中 能夠直接使用=來給委託對象複製 而在事件中就不能直接使用= 要使用+= 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); } } }
上面能夠看到,使用事件來實現了一樣的功能。
7、委託和代理設計模式的區別
不論是使用委託或者事件其實它們都是在A對象(本例中的Person對象)中調用B對象中的方法,這與設計模式中有類似之處。具體代理設計模式這裏就省略了,委託和代理都是在A對象使用B對象中的方法。不過它們仍是有區別的,委託中在A中直接使用的是B中的方法,是類與方法之間的,代理設計模式中是將A類中設置一個B類變量,而後經過B來使用B中的方法,是類與類之間的。這也是個人我的理解,不知道對不對,錯了的話也但願大牛指正,以避免耽誤了其餘的社會主義接班人。