本篇文章主要介紹委託的應用。html
委託是你們最多見的語法了,但會用與精通之間的差異是巨大的。程序員
一個程序員若是不能精通委託,那麼,他永遠沒法成爲高級程序員。架構
因此,讓咱們把委託刻到血液裏吧。框架
這樣,你才能稱爲[Developer]。異步
委託的定義async
什麼是委託?函數
委託其實是一種類型,是一種引用類型。學習
微軟用delegate關鍵字來聲明委託,delegate與int,string,double等關鍵字同樣。都是聲明用的。spa
下面先看下聲明代碼,這裏聲明瞭兩個委託。線程
public delegate void TestDelegate(string message); public delegate int TestDelegate(MyType m, long num);
delegate既然是關鍵字,和int,string同樣,那麼,爲何delegate後又跟了一個void或者int呢?
若是他們是同等地位的關鍵字,爲何能夠一塊兒使用呢?
很簡單,咱們把delegate後面的 【void TestDelegate(string message)】理解爲一個變量,是否是就清晰明瞭了一些。
咱們把delegate關鍵字理解爲,是用來專門來定義這種複雜的變量的。而這種複雜的變量能夠包含一個返回值和任意數目任意類型的傳入參數。
有沒有感受,這個複雜的變量特別像一個函數的定義。
沒錯,官方定義,委託類型的聲明與方法簽名類似。因此,這個複雜變量,的確,書寫的方式就是與函數同樣。
那麼,爲何這個聲明方式如此怪異呢,是由於,咱們用delegate定義的變量,只能用函數賦值。賦值方式以下所示:
public delegate void TestDelegate(string message); public delegate long TestDelegate2(int m, long num); public static void Excute() { TestDelegate2 td = Double; } static long Double(int m, long num) { return m * num; }
委託的基本應用
學會了賦值之後,我開始使用委託。
委託的使用方式以下:
string result = td(51, 8); Console.WriteLine(result);
這裏咱們會發現,委託的使用方式與函數調用同樣。
沒錯,它們的確是同樣的。由於委託是用函數來賦值的,因此調用方式同樣也並不奇怪,不是嗎。
換一種說法,就是委託封裝了一個函數。
若是委託是封裝的函數,而且它又是引用類型。那麼委託第一種常規的應用就浮現出來了。
那就是——引用類型的函數。
若是函數是引用類型,那麼這個函數只要沒被內存回收,就能夠被調用。若是是public函數或者是public static函數,那麼它能跨越的東西就更多了。
好比能夠跨類調用,跨程序集調用等等。而這種用法,就是委託的基本應用。
匿名委託的應用
匿名委託的官方介紹:在 2.0 以前的 C# 版本中,聲明委託的惟一方式是使用命名方法。 C# 2.0 引入匿名方法,在 C# 3.0 及更高版本中,Lambda 表達式取代匿名方法做爲編寫內聯代碼的首選方式。
看不懂不要緊,咱們直接來學習使用。代碼以下:
delegate string anonymousDelegate(int m, long num); public static void Excute() { anonymousDelegate ad = delegate (int m, long num) { return m.ToString() + num.ToString(); };//2.0時代的匿名委託 anonymousDelegate ad2 = (m, num) => { return m.ToString() + num.ToString(); };//3.0之後匿名委託 }
如代碼所示,匿名委託是Lambda表達式,不懂的同窗就當它是有固定寫法便可,不用講什麼道理,只要記住並應用便可。
匿名委託雖然減小了一點代碼,但仍是要求咱們本身去聲明委託。全部,還能再簡寫一點嗎?
答案固然是,能夠的。
Action與Func
Action與Func是微軟爲咱們預先定義好了的,兩個委託變量。其中Action是不帶返回值的委託,Func是帶返回值的委託。
能夠說,Action與Func徹底包含了,咱們平常使用所需的,所有的,委託變量。
也就是說,咱們能夠不用再去本身手動聲明委託了。
下面來看最簡單的Action與Func的定義:
Action a1 = () => { }; Func<int> f1 = () => { return 1; };//必須寫 return 1;
Action與Func是泛型委託,各支持16個入參變量。下面代碼爲一個入參的定義,多參數以此類推。
Action<int> a1 = (i) => { }; Func<string,int> f1 = (str) => { return 1;//必須寫 return 1; };
委託的線程應用
委託的線程應用是委託的第二種用法,分爲線程使用委託,和委託的異步應用兩種。
咱們先看線程使用委託。以下代碼所示,一個無入參匿名Action和一個無入參匿名Func。
Task taskAction = new Task(() => { });//無入參匿名Action taskAction.Start(); Task<int> taskFunc = new Task<int>(() => { return 1; });//無入參匿名Func taskFunc.Start(); int result= taskFunc.GetAwaiter().GetResult();//獲取線程返回結果
咱們能看到兩種委託應用,代碼都很是簡潔。
下面咱們再來看委託的異步應用。首先看最簡單的異步調用。
Action action = new Action(() => { }); IAsyncResult result = action.BeginInvoke((iar) => { }, null); Func<int> func = new Func<int>(() => { return 1; }); IAsyncResult resultfunc = func.BeginInvoke((iar) => { var res = func.EndInvoke(iar); }, null);
這裏咱們使用委託的BeginInvoke方法來開啓線程,進行異步調用。如上面代碼所示,這裏介紹了Action與Func的最基礎的異步應用。
委託,架構的血液
委託是架構的血液,若是系統中沒有委託,那代碼將堆疊到一塊兒,比大力膠粘的都緊密。
就比如一碗湯麪倒掉了全部的湯,只要它靜放一個陣子,就會變成一坨面球,讓你無從下嘴。
因此,委託是架構的血液,是框架的流暢的基石。
那麼委託究竟是如何流動的呢?
咱們先從剛介紹過的委託的線程應用提及。
----------------------------------------------------------------------------------------------------
第一核心應用——隨手線程:
咱們在作開發的時候,必定接觸過父類。父類是幹什麼的呢?父類一般是用來編寫公共屬性和函數,方便子類調用的。
那咱們的委託的第一個核心應用,就是父類的公共函數,線程隨手啓動。如何隨手開啓呢?
首先,咱們建立父類代碼以下:
class BaseDelegateSyntax { public void AsyncLoad(Action action) { } public void AsyncLoad(Action action, Action callback) { IAsyncResult result = action.BeginInvoke((iar) => { callback(); }, null); } public void AsyncLoad<T>(Action<T> action, T para, Action callback) { IAsyncResult result = action.BeginInvoke(para, (iar) => { callback(); }, null); } public void AsyncLoad<T, R>(Func<T, R> action, T para, Action<R> callback) { IAsyncResult result = action.BeginInvoke(para, (iar) => { var res = action.EndInvoke(iar); callback(res); }, null); } }
咱們看到上面的代碼,父類中添加了四個異步委託的調用函數,接下來,咱們就能夠在繼承該類的子類中,隨手開啓線程了。
子類代碼以下:
class ChildDelegateSyntax : BaseDelegateSyntax { public void Excute() { //開啓異步方法 base.AsyncLoad(() => { }); //開啓異步方法,而且在異步結束後,觸發回調方法 base.AsyncLoad(() => { }, ()=> { //我是回調方法 }); //開啓異步有入參的方法,傳遞參數,而且在異步結束後,觸發回調方法 base.AsyncLoad<string>((s) => { },"Kiba518", () => { //我是回調方法 }); //開啓異步有入參的方法,傳遞字符串參數Kiba518,以後返回int型結果518, //而且在異步結束後,觸發回調方法,回調函數中能夠得到結果518 base.AsyncLoad<string,int>((s) => { return 518; }, "Kiba518", (result) => { //我是回調方法 result是返回值518 }); } }
看了上面的父子類後,是否感受委託讓咱們繁雜的線程世界變簡潔了呢?
----------------------------------------------------------------------------------------------------
第二核心應用——穿越你的世界:
接下來,咱們來看委託的第二種核心用法,穿越的應用。
這個應用,是最多見,也最普通的應用了。由於委託是引用類型,因此A類裏定義的委託,能夠在被內存回收以前,被其餘類調用。
咱們常常會在各類論壇看到有人發問,A頁面如何調用B頁面的屬性、方法、父頁面獲取子頁面的屬性、方法,或者子頁面獲取父頁面的屬性、方法。
其實,只要定義好委託,並將委託正確的傳遞,就能夠實現穿越的調用了。
下面咱們看下穿越應用的代碼。
public class FirstDelegateSyntax { public FirstDelegateSyntax() { Console.WriteLine(" First 開始 " ); SecondDelegateSyntax sds = new SecondDelegateSyntax(()=> { Console.WriteLine(" First傳給Second委託被觸發 "); }); sds.Excute(); Console.WriteLine(" First 結束 "); } } public class SecondDelegateSyntax { public Action Action { get; set; } public SecondDelegateSyntax(Action _action) { Console.WriteLine(" Second的構造函數 "); Action = _action; } public void Excute() { Console.WriteLine(" Second的Excute被觸發 "); Action(); } }
咱們能夠看到,咱們傳遞的委託,穿越了自身所屬的類。在SecondDelegateSyntax類中被觸發了。
運行結果以下:
第三核心應用——回調函數:
世界上本沒有回調函數,叫的人多了,也就有了。
請記住,全部的回調函數,都是委託的穿越應用,全部的回調函數;都是委託的穿越應用;全部的回調函數,都是委託的穿越應用。
重要的話要講三遍。
由於委託是引用類型,因此能夠被[址傳遞]。函數是不能夠被傳遞的。
當你傳遞函數的時候,實際上是匿名傳遞了一個委託的地址。
結語
委託是咱們最經常使用的語法,它將函數封裝成引用類型的變量,供其餘單位調用。
由於委託的特質是引用類型,因此決定了委託是能夠進行址傳遞。也就是說,委託是穿梭於咱們系統代碼中的列車。
咱們能夠在列車上放不少不少東西,在須要的站點,叫停列車,並將託運的東西搬下來使用。
因此,理論上,只要咱們利用好委託,就能夠大量減小冗餘的代碼。
但委託這種列車,是每一個程序員均可以定義的,若是一個項目中有十個開發者,每一個人都在定義委託,那麼,就有可能出現定義了十個相同的委託的狀況,這樣就出現了撞車的現象。
因此委託在使用的時候,儘可能作到有序傳遞,即預先作好列車的行駛路線,讓委託按照路徑運行。儘可能不要定義能夠被任何單位調用的公共委託。
若是須要公共委託,能夠採起反射的方式來調用。
後面我會繼續寫事件,消息,反射等語法,敬請期待。
----------------------------------------------------------------------------------------------------
注:此文章爲原創,任何形式的轉載都請聯繫做者得到受權並註明出處!
若您以爲這篇文章還不錯,請點擊下方的【推薦】,很是感謝!