您好,登錄后才能下訂單哦!
看C#高級編程第八版看到委托,有人說它和Java中的代理有些相似,但我認為這是一個C#與其他編程語言不同的地方,這也應該很重要,不然書上也不會大篇幅的講委托的概念和應用。我在網上查閱了關于委托的資料,發現它們就像是一道檻兒,過了這個檻的人,覺得真是太容易了,而沒有過去的人每次見到委托和事件就覺得心里憋得慌,混身不自在。
那初次看見“委托”這個詞,我腦海中出現的第一個疑問就是:
1.什么是委托?
我看完整這個章節,依然是一知半解的,功夫不負有心人,通過強大的網路,我尋找到了答案,那到底什么是委托呢?
委托就是可以把方法作為參數傳遞,也就是說通過委托可以像操作變量一樣的操作方法參數。看到這里我發現這并不是C#首創的這個概念,因為在C++中就有函數指針的概念和應用,我理解這其實就是微軟把C++中的函數指針作為原型而衍生出來的,經過封裝的這么一個特殊概念,其實現在我看來也不是很特別嗎,畢竟在C++中曾經學過呀。
現在我已經知道了委托的概念了,那么我第二個問題又來了。
2.這個委托怎么用呢?
我覺得怎么用,還是要拿Demo來進行講解會比較方便理解和對比。下面是我參看網絡上的大神們所用到代碼,我們不討論所給Demo的代碼是否有實際意義,只是為了讓讀者能夠更加方便的理解什么叫委托。代碼如下:
a.現在要模仿人打招呼。那么我們需要知道他的名字方法如下:
public static void ChineseGreeting(string name){ Console.WriteLine("早上好!"+name); } static void Main(string[] args) { ChineseGreeting("李雷"); Console.ReadKey(); }
b.要是現在要給一個老外(不懂中文的)打招呼,那要這怎么辦呢?是不是應該知道給老外說英文呢?單獨創建一個方法呢?不過在創建方法之前,我們需要先用一個枚舉類型去判讀是外國人還是中國人。
代碼如下:
public enum Language{ Chinese,English } public void EnglishGreeting(string name){ Console.WriteLine("Morning !"+name); } public GreatPeople(string name,Language language){ switch(language) { case language.English:EnglishGreeting(name);break; case language.Chinese:ChineseGreeting(name);break; } }
只要有些編程經驗的人就知道,上面的解決方案不好,如果下次有日本人,有韓國人,非洲人等等,那么我們是不是就又要去添加枚舉元素,添加問候方法!上面的方法不利于擴展。所以我們就想有沒有什么可以不用這么麻煩而且能很好的擴展呢?我現在分析上面的解決方案是如何做到的。
首先添加一個對應語言的問候的方法是不可少的,枚舉元素也是必須的,枚舉元素是為了判斷調用哪個語言的問候方法去問候的標志,那么既然我們前面提到了方法可以做參數那為什么不直接傳方法名稱呢?下面就是利用代理實現上面方案的代碼如下:
public delegate void GreetingDelegate(string name); class Program { public static void EnglishGreeting(string name) { Console.WriteLine("Morning!"+name); } public static void ChineseGreeting(string name) { Console.WriteLine("早上好!" + name); } //這是一個 public static void GreetingPeople(string name, GreetingDelegate greetingmethod) { greetingmethod(name);// } static void Main(string[] args) { GreetingPeople("Mike", EnglishGreeting); GreetingPeople("李雷", ChineseGreeting); Console.ReadKey(); } }
看上面的代碼,是不是感覺舒服多了我們不用使用枚舉了,也不用進行選擇(switch)了。
請注意:public static void GreetingPeople(string name, GreetingDelegate greetingmethod)這個方法中使用了一個委托類型的參數傳遞使用的問候方法。是不是感覺很神奇。
看到上面的Demo你是不是對委托有些感覺了呢?現在我的問題又來了。
3.方法是如何綁定到委托的?
這個問題我也需要借用上面的Demo進行修改一下,以便于顯示效果。Demo代碼如下:
public delegate void GreetingDelegate(string name); class Program { public static void EnglishGreeting(string name) { Console.WriteLine("Morning,"+name); } public static void ChineseGreeting(string name) { Console.WriteLine("早上好!" + name); } public static void GreetingPeople(string name, GreetingDelegate greetingmethod) { Console.WriteLine("問候開始..."); greetingmethod(name); Console.WriteLine("問候結束!"); } static void Main(string[] args) { GreetingDelegate delegate1;//委托實例 delegate1 = EnglishGreeting; delegate1 += ChineseGreeting; GreetingPeople("Mike", EnglishGreeting); GreetingPeople("李雷", ChineseGreeting); GreetingPeople("John", delegate1); Console.ReadKey(); } }
結果如下:
多個方法可以被綁定到一個委托實例中去。
注釋:delegate1 = EnglishGreeting;這個操作符"="是初始化委托實例,那么"+="是綁定語法。那么到底
初始化和綁定還可以這樣做:
GreetingDelegate delegate1; delegate1 = EnglishGreeting; delegate1 += ChineseGreeting; GreetingPeople("John", delegate1); GreetingDelegate delegate2 = new GreetingDelegate(EnglishGreeting); delegate2 += ChineseGreeting; GreetingPeople("LiLy", delegate2); Console.ReadKey();
這樣的看,委托是不是和類非常的相似!你先下面這段代碼和信息你會發現越發的像。
GreetingDelegate delegate3 = new GreetingDelegate();//報錯,不含有0個參數的構造函數 delegate3 += EnglishGreeting; Console.ReadKey();
說明編譯器在編譯的時候會把委托當做類進行編譯。這一點也是函數指針比不了的。
總結:
GreetingDelegate
1.委托定義
委托的聲明原型是
delegate <函數返回類型> <委托名> (<函數參數>)
例子:public delegate void GreetingDelegate(string name);//定義了一個委托它可以注冊返回void類型且有一個int作為參數的函數這樣就定義了一個委托,但是委托在.net內相當于聲明了一個類,類如果不實例化為對象,很多功能是沒有辦法使用的,委托也是如此.
2.委托實例化
委托實例化的原型是
<委托類型> <實例化名>=new <委托類型>(<注冊函數>)
例子:CheckDelegate _checkDelegate=new CheckDelegate(CheckMod);//用函數CheckMod實例化上面的CheckDelegate 委托為_checkDelegate
在.net 2.0開始可以直接用匹配的函數實例化委托:
<委托類型> <實例化名>=<注冊函數>
例子:CheckDelegate _checkDelegate=CheckMod;//用函數CheckMod實例化上面的CheckDelegate 委托為_checkDelegate現在我們就可以像使用函數一樣來使用委托了,在上面的例子中現在執行_checkDelegate()就等同于執行CheckMod(),最關鍵的是現在函數CheckMod相當于放在了變量當中,它可以傳遞給其它的CheckDelegate引用對象,而且可以作為函數參數傳遞到其他函數內,也可以作為函數的返回類型
3.用匿名函數初始化委托
上面為了初始化委托要定義一個函數是不是感覺有點麻煩,另外被賦予委托的函數一般都是通過委托實例來調用,很少會直接調用函數本身。
在.net 2.0的時候考慮到這種情況,于是匿名函數就誕生了,由于匿名函數沒有名字所以必須要用一個委托實例來引用它,定義匿名函數就是為了初始化委托
匿名函數初始化委托的原型:
<委托類型> <實例化名>=new <委托類型>(delegate(<函數參數>){函數體});
當然在.net 2.0后可以用:
<委托類型> <實例化名>=delegate(<函數參數>){函數體};
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)); }
當然在.net 3.0的時候又有了比匿名函數更方便的東西lambda表達式,這兒就不說了。
4.泛型委托
委托也支持泛型的使用
泛型委托原型:
delegate <T1> <委托名><T1,T2,T3...> (T1 t1,T2 t2,T3 t3...)
例子:
delegate T2 A<T1,T2>(T1 t);//定義有兩個泛型(T1,T2)的委托,T2作為委托函數返回類型,T1 作為委托函數參數類型
static int test(int t)
{
return t;
}
static void Main(string[] args)
{
A<int, int> a =test;//將泛型委托委托<T1,T2>實例化為<int,int>,即表示有一個int類型 參數且返回類型是int的函數,所以將test用來實例化委托
Console.WriteLine(a(5));//輸出5
}
5.委托的多播性
在上面實例化委托的時候看到:必須將一個匹配函數注冊到委托上來實例化一個委托對象,但是一個實例化委托不僅可以注冊一個函數還可以注冊多個函數,注冊多個函數后,在執行委托的時候會根據注冊函數的注冊先后順序依次執行每一個注冊函數
函數注冊委托的原型:
<委托類型> <實例化名>+=new <委托類型>(<注冊函數>)
例子:CheckDelegate _checkDelegate=new CheckDelegate(CheckMod);//將函數CheckMod注冊到委托實例_checkDelegate上
在.net 2.0開始可以直接將匹配的函數注冊到實例化委托:
<委托類型> <實例化名>+=<注冊函數>
例子:CheckDelegate _checkDelegate+=CheckMod;//將函數CheckMod注冊到委托實例_checkDelegate上
之后我們還可以注冊多個函數到委托上:
例子:_checkDelegate+=CheckPositive;//將函數CheckPositive注冊到委托實例_checkDelegate上
_checkDelegate();//執行這個委托實例會先執行CheckMod()再執行CheckPositive()
實際上使用+=符號的時候會判斷
如果此時委托還沒有實例化(委托實例為null),它會自動用+=右邊的函數實例化委托
如果此時委托已經實例化,它會只把+=右邊的函數注冊到委托實例上
另外有一點需要注意的是,如果對注冊了函數的委托實例從新使用=號賦值,相當于是重新實例化了委托,之前在上面注冊的函數和委托實例之間也不再產生任何關系,后面的例子會講到這點!
當然有+=注冊函數到委托,也有-=解除注冊
例子:_checkDelegate-=new CheckDelegate(CheckPositive);//解除CheckPositive對_checkDelegate的注冊
_checkDelegate-=CheckPositive;//.net 2.0開始可以用這種方式解除注冊
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。