委托的定義
什么是委托?
委托實際上是一種型別,是一種參考型別,
微軟用delegate關鍵字來宣告委托,delegate與int,string,double等關鍵字一樣,都是宣告用的,
下面先看下宣告代碼,這里宣告了兩個委托,
| 1 2 |
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定義的變數,只能用函式賦值,賦值方式如下所示:
| 1 2 3 4 5 6 7 8 9 10 |
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;
}
|
委托的基本應用
學會了賦值以后,我開始使用委托,
委托的使用方式如下:
| 1 2 |
string result = td(51, 8);
Console.WriteLine(result);
|
這里我們會發現,委托的使用方式與函式呼叫一樣,
沒錯,它們的確是一樣的,因為委托是用函式來賦值的,所以呼叫方式一樣也并不奇怪,不是嗎,
換一種說法,就是委托封裝了一個函式,
如果委托是封裝的函式,并且它又是參考型別,那么委托第一種常規的應用就浮現出來了,
那就是——參考型別的函式,
如果函式是參考型別,那么這個函式只要沒被記憶體回收,就可以被呼叫,如果是public函式或者是public static函式,那么它能跨越的東西就更多了,
比如可以跨類呼叫,跨程式集呼叫等等,而這種用法,就是委托的基本應用,
匿名委托的應用
匿名委托的官方介紹:在 2.0 之前的 C# 版本中,宣告委托的唯一方式是使用命名方法, C# 2.0 引入匿名方法,在 C# 3.0 及更高版本中,Lambda 運算式取代匿名方法作為撰寫行內代碼的首選方式,
看不懂沒關系,我們直接來學習使用,代碼如下:
| 1 2 3 4 5 6 |
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的定義:
| 1 2 |
Action a1 = () => { };
Func<int> f1 = () => { return 1; };//必須寫 return 1;
|
Action與Func是泛型委托,各支持16個入參變數,下面代碼為一個入參的定義,多引數以此類推,
| 1 2 |
Action<int> a1 = (i) => { };
Func<string,int> f1 = (str) => { return 1;//必須寫 return 1; };
|
委托的執行緒應用
委托的執行緒應用是委托的第二種用法,分為執行緒使用委托,和委托的異步應用兩種,
我們先看執行緒使用委托,如下代碼所示,一個無入參匿名Action和一個無入參匿名Func,
| 1 2 3 4 5 |
Task taskAction = new Task(() => { });//無入參匿名Action
taskAction.Start();
Task<int> taskFunc = new Task<int>(() => { return 1; });//無入參匿名Func
taskFunc.Start();
int result= taskFunc.GetAwaiter().GetResult();//獲取執行緒回傳結果
|
我們能看到兩種委托應用,代碼都非常簡潔,
下面我們再來看委托的異步應用,首先看最簡單的異步呼叫,
| 1 2 3 4 5 6 7 8 9 10 |
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的最基礎的異步應用,
委托,架構的血液
委托是架構的血液,如果系統中沒有委托,那代碼將堆疊到一起,比大力膠粘的都緊密,
就好比一碗湯面倒掉了所有的湯,只要它靜放一個陣子,就會變成一坨面球,讓你無從下嘴,
所以,委托是架構的血液,是框架的流暢的基石,
那么委托到底是如何流動的呢?
我們先從剛介紹過的委托的執行緒應用說起,
----------------------------------------------------------------------------------------------------
第一核心應用——隨手執行緒:
我們在做開發的時候,一定接觸過父類,父類是干什么的呢?父類通常是用來撰寫公共屬性和函式,方便子類呼叫的,
那我們的委托的第一個核心應用,就是父類的公共函式,執行緒隨手啟動,如何隨手開啟呢?
首先,我們創建父類代碼如下:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
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);
}
}
|
我們看到上面的代碼,父類中添加了四個異步委托的呼叫函式,接下來,我們就可以在繼承該類的子類中,隨手開啟執行緒了,
子類代碼如下:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
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頁面的屬性、方法、父頁面獲取子頁面的屬性、方法,或者子頁面獲取父頁面的屬性、方法,
其實,只要定義好委托,并將委托正確的傳遞,就可以實作穿越的呼叫了,
下面我們看下穿越應用的代碼,
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
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類中被觸發了,
運行結果如下:

第三核心應用——回呼函式:
世界上本沒有回呼函式,叫的人多了,也就有了,
請記住,所有的回呼函式,都是委托的穿越應用,所有的回呼函式;都是委托的穿越應用;所有的回呼函式,都是委托的穿越應用,
重要的話要講三遍,
因為委托是參考型別,所以可以被[址傳遞],函式是不可以被傳遞的,
當你傳遞函式的時候,其實是匿名傳遞了一個委托的地址,
結語
委托是我們最常用的語法,它將函式封裝成參考型別的變數,供其他單位呼叫,
因為委托的特質是參考型別,所以決定了委托是可以進行址傳遞,也就是說,委托是穿梭于我們系統代碼中的列車,
我們可以在列車上放很多很多東西,在需要的站點,叫停列車,并將托運的東西搬下來使用,
所以,理論上,只要我們利用好委托,就可以大量減少冗余的代碼,
但委托這種列車,是每個程式員都可以定義的,如果一個專案中有十個開發者,每個人都在定義委托,那么,就有可能出現定義了十個相同的委托的情況,這樣就出現了撞車的現象,
所以委托在使用的時候,盡量做到有序傳遞,即預先做好列車的行駛路線,讓委托按照路徑運行,盡量不要定義可以被任何單位呼叫的公共委托,
如果需要公共委托,可以采取反射的方式來呼叫,
后面我會繼續寫事件,訊息,反射等語法,敬請期待,
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/94886.html
標籤:C#
上一篇:C# read file to bytes,File.ReadAllFiles,File.Open(),BinaryReader
