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

来源:https://www.cnblogs.com/kiba/archive/2018/07/23/9330936.html
-Advertisement-
Play Games

本篇文章主要介紹委托的應用。 委托是大家最常見的語法了,但會用與精通之間的差距是巨大的。 一個程式員如果不能精通委托,那麼,他永遠無法成為高級程式員。 所以,讓我們把委托刻到血液里吧。 這樣,你才能稱為Developer。 ...


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

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

一個程式員如果不能精通委托,那麼,他永遠無法成為高級程式員。

所以,讓我們把委托刻到血液里吧。

這樣,你才能稱為[Developer]。

委托的定義

什麼是委托?

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

微軟用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;
}

委托的基本應用

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

委托的使用方式如下:

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

運行結果如下:

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

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

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

重要的話要講三遍。

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

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

結語

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

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

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

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

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

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

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

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

----------------------------------------------------------------------------------------------------

註:此文章為原創,歡迎轉載,請在文章頁面明顯位置給出此文鏈接!
若您覺得這篇文章還不錯,請點擊下右下角的推薦】,非常感謝!
如果您覺得這篇文章對您有所幫助,那就不妨小小打賞一下吧。 

 


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

-Advertisement-
Play Games
更多相關文章
  • 註:希望與各位讀者相互交流,共同學習進步。 ...
  • 做為一個略微看過nodejs語法,但又不懂nodejs的攻城獅,搭建hexo環境很是麻煩,要考慮到翻牆、版本相容等問題。於是乎,博主每換一個電腦,為了能繼續發博客,都需要在新電腦上花一天時間重新搞一下 hexo 環境,樓主感覺還是有簡潔的方案來實現我一提交代碼就可以自動發佈博客,不需要再手動操作一波 ...
  • __author__ = 'zht' #!/usr/bin/env python # -*- coding: utf-8 -*- ''' #努力學習每一天 ''' import operator #方法1 a = [1,2,3,4,5,6,7,11,10,9,] i = 1 b = a[0] whi... ...
  • 原創 裸一篇圖的BFS遍歷,直接來圖: 簡單介紹一下BFS遍歷的過程: 以上圖為例子,從0開始遍歷,訪問0,按大小順序訪問與0相鄰的所有頂點,即先訪問1,再訪問2; 至此頂點0已經沒有作用了,因為其本身和與其所有相鄰的頂點都已被訪問,將其出隊列,我們用隊列 存儲已訪問過的頂點;然後順著隊列,訪問頂點 ...
  • 發現一個奇怪的現象,我以Python入門為題的隨筆文章莫名其妙有幾十的閱讀量,讓我有點尷尬,其實大家如果想學Python的話,可以關註一些公眾號,比如「一個程式員的日常」和「痴海」等等,他們都有Python方面的學習資源,而且他們也會推薦一些優質的公眾號供大家學習,關註一下,如果之後感覺不怎樣,取關 ...
  • IO流和Properties IO流 IO流是指電腦與外部世界或者一個程式與電腦的其餘部分的之間的介面。它對於任何電腦系統都非常關鍵, 因而所有 I/O 的主體實際上是內置在操作系統中的。單獨的程式一般是讓系統為它們完成大部分的工作。 在 Java 編程中,一直使用流的方式完成 I/O。所有 ...
  • 註:希望與各位讀者相互交流,共同學習進步。 ...
  • 三大結構 順序 分支 迴圈 三大結構 順序 分支 迴圈 三大結構 順序 分支 迴圈 三大結構 順序 分支 迴圈 三大結構 順序 分支 迴圈 三大結構 順序 分支 迴圈 三大結構 順序 分支 迴圈 三大結構 順序 分支 迴圈 三大結構 順序 分支 迴圈 分支 分支的基本語法 if 條件表達式: 語句1 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...