C#語法--委托,架構的血液

来源:https://www.cnblogs.com/ljdong7/archive/2019/12/09/12010465.html

委托的定義 什麼是委托? 委托實際上是一種類型,是一種引用類型。 微軟用delegate關鍵字來聲明委托,delegate與int,string,double等關鍵字一樣。都是聲明用的。 下麵先看下聲明代碼,這裡聲明瞭兩個委托。 1 2 public delegate void TestDelega ...


委托的定義

什麼是委托?

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

微軟用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類中被觸發了。

運行結果如下:

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

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

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

重要的話要講三遍。

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

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

結語

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

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

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

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

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

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

如果需要公共委托,可以採取反射的方式來調用。

後面我會繼續寫事件,消息,反射等語法,敬請期待。


您的分享是我們最大的動力!

更多相關文章
一周排行
  • 比如要拆分“呵呵呵90909086676喝喝999”,下麵當type=0返回的是中文字元串“呵呵呵,喝喝”,type=1返回的是數字字元串“90909086676,999”, private string GetStrings(string str,int type=0) { IList<strin ...
  • Swagger一個優秀的Api介面文檔生成工具。Swagger可以可以動態生成Api介面文檔,有效的降低前後端人員關於Api介面的溝通成本,促進項目高效開發。 1、使用NuGet安裝最新的包:Swashbuckle.AspNetCore。 2、編輯項目文件(NetCoreTemplate.Web.c ...
  • 2020 年 7 月 30 日, 由.NET基金會和微軟 將舉辦一個線上和為期一天的活動,包括 微軟 .NET 團隊的演講者以及社區的演講者。本次線上大會 專註.NET框架構建微服務,演講者分享構建和部署雲原生應用程式的最佳實踐、模式、提示和技巧。有關更多信息和隨時瞭解情況:https://focu... ...
  • #abp框架Excel導出——基於vue #1.技術棧 ##1.1 前端採用vue,官方提供 UI套件用的是iview ##1.2 後臺是abp——aspnetboilerplate 即abp v1,https://github.com/aspnetboilerplate/aspnetboilerp ...
  • 前言 本文的文字及圖片來源於網路,僅供學習、交流使用,不具有任何商業用途,版權歸原作者所有,如有問題請及時聯繫我們以作處理。 作者:碧茂大數據 PS:如有需要Python學習資料的小伙伴可以加下方的群去找免費管理員領取 input()輸入 Python提供了 input() 內置函數從標準輸入讀入一 ...
  • 從12年到20年,python以肉眼可見的趨勢超過了java,成為了當今It界人人皆知的編程語言。 python為什麼這麼火? 網路編程語言搜索指數 適合初學者 Python具有語法簡單、語句清晰的特點,這就讓初學者在學習階段可以把精力集中在編程對象和思維方法上。 大佬都在用 Google,YouT ...
  • 在社會上存在一種普遍的對培訓機構的學生一種歧視的現象,具體表現在,比如:當你去公司面試的時候,一旦你說了你是培訓機構出來的,那麼基本上你就涼了,那麼你瞞著不說,然後又通過了面試成功入職,但是以後一旦在公司被髮現有培訓經歷,可能會面臨被降薪,甚至被辭退,培訓機構出來的學生,在用人單位眼裡就是能力低下的 ...
  • from typing import List# 這道題看了大佬寫的代碼,經過自己的理解寫出來了。# 從最外圍的四周找有沒有為O的,如果有的話就進入深搜函數,然後深搜遍歷# 判斷上下左右的位置是否為Oclass Solution: def solve(self, board: List[List[s ...
  • import requests; import re; import os; # 1.請求網頁 header = { "user-agent":'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_5) AppleWebKit/537.36 (KHTML, li ...
  • import requests; import re; import os; import parsel; 1.請求網頁 header = { "user-agent":'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_5) AppleWebKit/537. ...