C#語法——委託,架構師必修

C#語法——委託,架構的血液

本篇文章主要介紹委託的應用。

委託是大家最常見的語法了,但會用與精通之間的差別是巨大的。

一個程序員如果不能精通委託,那麼,他永遠無法成為一個架構師。

所以,委託是必須掌握的技能之一。

委託的定義

什麼是委託?

委託實際上是一種類型,是一種引用類型。

微軟用delegate關鍵字來聲明委託,delegate與int,string,double等關鍵字一樣。都是聲明用的。

下面先看下聲明代碼,這裡聲明瞭兩個委託。

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;
}

委託的基本應用

學會了賦值以後,我開始使用委託。

委託的使用方式如下:

public static void Excute()
{

TestDelegate2 td = Double;
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 f1 = () => { return 1; };//必須寫 return 1;

Action與Func是泛型委託,最多支持16個入參變量。下面代碼為一個入參的定義,多參數以此類推。

Action a1 = (i) => { };
Func<string> f1 = (str) => { return 1;//必須寫 return 1; };
/<string>

委託的線程應用

委託的線程應用是委託的第二種用法,分為線程使用委託,和委託的異步應用兩種。

我們先看線程使用委託。如下代碼所示,一個無入參匿名Action和一個無入參匿名Func。

Task taskAction = new Task(() => { });//無入參匿名Action
taskAction.Start();

Task taskFunc = new Task(() => { return 1; });//無入參匿名Func
taskFunc.Start();

int result= taskFunc.GetAwaiter().GetResult();//獲取線程返回結果

我們能看到兩種委託應用,代碼都非常簡潔。

下面我們再來看委託的異步應用。首先看最簡單的異步調用。

Action action = new Action(() => { });
IAsyncResult result = action.BeginInvoke((iar) =>
{
}, null);

Func func = new Func(() => { 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(Action action, T para, Action callback)
{
IAsyncResult result = action.BeginInvoke(para, (iar) =>
{
callback();
}, null);
}

public void AsyncLoad(Func action, T para, Action 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>((s) => {
return 518;
}, "Kiba518",
(result) =>
{
//我是回調方法 result是返回值518
});
}
}
/<string>/<string>

看了上面的父子類後,是否感覺委託讓我們繁雜的線程世界變簡潔了呢?

第二核心應用——穿越你的世界:

接下來,我們來看委託的第二種核心用法,穿越的應用。

這個應用,是最常見,也最普通的應用了。因為委託是引用類型,所以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類中被觸發了。

運行結果如下:

C#語法——委託,架構師必修

第三核心應用——回調函數:

世界上本沒有回調函數,叫的人多了,也就有了。

請記住,所有的回調函數,都是委託的穿越應用,所有的回調函數;都是委託的穿越應用;所有的回調函數,都是委託的穿越應用。

重要的話要講三遍。

因為委託是引用類型,所以可以被[址傳遞]。函數是不可以被傳遞的。

當你傳遞函數的時候,其實是匿名傳遞了一個委託的地址。

結語

委託是我們最常用的語法,它將函數封裝成引用類型的變量,供其他單位調用。

因為委託的特質是引用類型,所以決定了委託是可以進行址傳遞。也就是說,委託是穿梭於我們系統代碼中的列車。

我們可以在列車上放很多很多東西,在需要的站點,叫停列車,並將託運的東西搬下來使用。

所以,理論上,只要我們利用好委託,就可以大量減少冗餘的代碼。

但委託這種列車,是每個程序員都可以定義的,如果一個項目中有十個開發者,每個人都在定義委託,那麼,就有可能出現定義了十個相同的委託的情況,這樣就出現了撞車的現象。

所以委託在使用的時候,儘量做到有序傳遞,即預先做好列車的行駛路線,讓委託按照路徑運行,儘量不要定義可以被任何單位調用的公共委託。

C#語法——委託,架構師必修

"


分享到:


相關文章: