主頁 > .NET開發 > C#基礎篇——委托

C#基礎篇——委托

2020-09-13 05:20:54 .NET開發

前言

在本章中,主要是借機這個C#基礎篇的系列整理過去的學習筆記、歸納總結并更加理解透徹,

在.Net開發中,我們經常會遇到并使用過委托,如果能靈活的掌握并加以使用會使你在編程中游刃有余,然后對于很多接觸C#時間不長的開發者而言,較好的理解委托和事件并不容易,

本節主要是講述對委托的定義、委托的使用、多播委托、泛型委托、匿名方法、Func和Action委托、Lambda委托,并對它們進行討論,

說明

簡單說它就是一個能把方法當引數傳遞的物件,而且還知道怎么呼叫這個方法,同時也是粒度最小的“介面”(約束了指向方法的簽名),

開始

1.定義委托

委托:是一種定義方法簽名的型別, 當實體化委托時,可以將其實體與任何具有兼容簽名的方法相關聯, 可以通過委托實體呼叫方法,

這里參考一個網友的說法:

某人有三子,讓他們各自帶一樣東西出門,并帶回一頭獵物,
上面一句話可以理解為父親對兒子的委托:獵物 辦法(工具 某工具)-->delegate 獵物(回傳值) 帶回獵物(委托名)(工具(引數型別) x)-->delegate int GetValue(int i)
三個人執行委托的方法各不相同
兔子 打獵(工具 弓)-public static int GetValue1(int i){ return i; }
野雞 買(工具 錢)-public static int GetValue2(int i){ return i*2; }
狼 誘捕(工具 陷阱)-public static int GetValue3(int i){ return i*i; }

2.簡單的使用

一個委托型別定義了該型別的實體化時能呼叫的一類方法,這些方法含有同樣的回傳型別和同樣引數(型別和引數個數相同)

比如:定義一個委托

delegate int Calculator (int x);

此委托適用于有著int回傳型別和一個int型別引數方法,

static int Double (int x) { return x * 2; }

創建一個委托實體,并將方法賦值給委托實體

Calculator c = new Calculator(Double);
//或者另一種寫法
Calculator c = Double;

通過委托實體的呼叫

int result = c(2);

3.多播委托

在開發中,我們有時候會遇到要通過呼叫一個委托,同時可以執行多個方法的時候,就可以考慮用多播委托,呼叫多個委托需要多次顯示呼叫這個委托,所有的委托實體都可以包含多個方法,實作多播功能,

這個打個比方:多播,就像一群程式員在瞬聘網填好了求職意向后,某天有個公司發布了一個和這些程式員求職意向剛好相匹配的作業,然后這些求職者都被通知了 - “有一份好作業招人啦,你們可以直接申請去上班了!”,
也就是說,一個委托實體不僅可以指向一個方法,還可以指向多個方法,
多播委托,提供了一種類似于流水線的鉤子機制,只要加載到這條流水線上的委托,都會被順序執行,因為所有的都繼承自MulticastDelegate,因此所有的委托都具有多播特性
        //宣告一個委托,委托回傳值為void
        public delegate void Greetings(String name);

        public static void Hello(String name)
        {
            Console.WriteLine("您好, {0}!", name);
        }

        public static void GoodBye(String name)
        {
            Console.WriteLine("再見, {0}!", name);
        }

        public static void Main()
        {
            Greetings greetings = Hello;
            //使用+=給委托添加方法
            greetings += GoodBye;
            String name = "艾三元";
            Console.WriteLine("這是一種呼叫方法:");
            //第一種執行方式
            greetings(name);
            //第二種執行方式
            Console.WriteLine("這是另一種使用方法");
            //回傳委托的呼叫串列,
            Delegate[] delegates = greetings.GetInvocationList();
            //注意這里的delegates串列中存盤的是Greetings型別的委托
            foreach (Greetings greeting in delegates)
            {
                greeting(name);
            }
            Console.ReadKey();
        }

1748233452

說明:

  • 如果是多播委托,委托的簽名就必須回傳 void ,否則,回傳值應送到何處?當委托只包含一個方法的時候,則可以通過所封裝的方法發現其回傳型別的宣告,不一定必須是void,實際上,如果編譯器發現某個委托回傳 void ,就會自動假定這是一個多播委托,

  • “+=” 用來添加,“-=”用來從委托中洗掉方法呼叫

4.泛型委托

在之前的篇章中,我們已經學會了什么是泛型,因此,也方便我們理解泛型委托,簡單的說,就是一種含有泛型引數的委托,

		public delegate T Calculator<T>(T arg);
        static int Double(int x) { return x * 2; }

        static class Utility
        {
            public static void Calculate<T>(T[] values, Calculator<T> c)
            {
                for (int i = 0; i < values.Length; i++)
                    values[i] = c(values[i]);
            }
        }
        static void Main(string[] args)
        {
            int[] values = { 11, 22, 33, 44 };

            Utility.Calculate(values, Double);
            foreach (int i in values)
                Console.Write(i + " "); // 22 44 66 88
            Console.ReadKey();
        }

5. 匿名方法

匿名方法,是在初始化委托時候行內宣告的方法,

每次實體化一個委托時,都需要事先定義一個委托所要呼叫的方法,為了簡化這個流程,C# 2.0開始提供匿名方法來實體化委托,這樣,我們在實體化委托時就可以 “隨用隨寫” 它的實體方法,

    static string GetNumber(string str)
    {
        return str;
    }
    delegate string DelNumber(string str);
    static void Main(string[] args)
    {
        //宣告一個名稱為GetNumber的具名方法
        DelNumber delNumber1 = GetNumber;
        Console.WriteLine(delNumber1("這是具名方法"));

        //匿名方法 ,未在別的地方定義方法,而是直接把方法寫在實體化代碼中
        DelNumber delNumber2 = delegate (string str)
        {
            return str;
        };
        Console.WriteLine(delNumber2("這是匿名方法呼叫"));
        Console.ReadKey();
    }

    #endregion

通過以上簡單的示例看出:

匿名方法的語法:關鍵字delegate {引數串列}{陳述句塊}

delegte { Paramters} {ImplementationCode}

		delegate (string str)
        {
            return str;
        };

使用的格式是:

委托類名 委托實體名 = delegate (args) {方法體代碼} ;

		 delegate string DelNumber(string str); //委托型別的回傳型別
        //匿名方法 ,未在別的地方定義方法,而是直接把方法寫在實體化代碼中
        DelNumber delNumber2 = delegate (string str)
        {
            return str;                         //根據回傳型別,回傳一個string型別
        };

這樣就可以直接把方法寫在實體化代碼中,不必在另一個地方定義方法,當然,匿名委托不適合需要采用多個方法的委托的定義,需要說明的是,匿名方法并不是真的“沒有名字”的,而是編譯器為我們自動取一個名字,

可以在以下地方使用匿名方法:

  • 宣告委托變數時為初始化運算式,
  • 組合委托時在賦值陳述句的右邊,
  • 為委托增加事件時在賦值陳述句的右邊,

6.Func 和 Action 委托

在之前,我們在使用委托的時候,都是自定義一個委托型別,再使用這個自定定義的委托定義一個委托欄位或變數,而在后續的編程語言中又新加入了一種特性,C#語言預先為我們定義了兩個常用的委托,一個是Func,一個是Action,還帶來了Lambda,這使得委托的定義和使用變得簡單起來, 在以后進行C#程式撰寫中引入委托更加靈活,

Action

C#中與預定義了一個委托型別Action,基本特點就是可以執行一個沒有回傳值,沒有引數的方法,是一類沒有輸出引數的委托,但是輸入引數可以為C#中的任意型別,即可以進行委托執行形式的方法,

    static void printString()
    {
        Console.WriteLine("Hello World");
    }
    static void printNumber(int x)
    {
        Console.WriteLine(x);
    }
    static void Main(String[] args)
    {
        //Action基本使用
        Action a = printString;
        a(); // 輸出結果  Hello World
        //Action指向有引數的方法
        Action<int> b = printNumber; // 定義一個指向 形參為int的函C#數
        b(5); // 輸出結果  5
    }

Action可以通過泛型來指定,指向的方法有 0 - 16個引數

Action<int, int, string, bool 等等>

Func

Func同樣也是預定的委托,是一種由回傳值的委托,傳遞0-16個引數,其中輸入引數和回傳值都用泛型表示,

  		static int GetNumber()
        {
            return 1;
        }
        static int GetNumber(string str)
        {
            return 1;
        }
        static void Main(string[] args)
        {
            Func<int> a = GetNumber; // 定義一個Func 委托,  指向一個回傳int型別的 方法
            Console.WriteLine(a());
            Func<string, int> b = GetNumber; // 泛型中最后一個引數表示回傳值型別,
            Console.WriteLine(b("Hello"));
        }	

注意:Func<string, int> 最后一個引數表示回傳值型別,前面的都是形參型別,

7. Lambda運算式

江山代有才人出,縱然匿名方法使用很方便,可惜她很快就成了過氣網紅,沒能領多長時間的風騷,如今已經很少見到了,因為delegate關鍵字限制了她用途的擴展,自從C# 3.0開始,她就被Lambda運算式取代,而且Lambda運算式用起來更簡單,Lambda運算式本質上是改進的匿名方法,

在匿名方法中,delegate關鍵字有點多余,因為編譯器已知將我們的方法賦值給委托,因此,我們很容易的將匿名方法的步驟轉換為Lambda運算式:1. 洗掉delegate關鍵字,2.在引數串列和匿名方法主體之間放lambda運算子=>,

DelNumber delNumber2 = delegate (string str){ return str;}; //匿名方法

DelNumber delNumber2 =  (string str) =>{ return str;}; //Lambda方法

Lambda運算式的靈感來源于數學中的Lambda積分函式運算式,例如下圖:

1270751451

Lambda運算式把其中的箭頭用 => 符號表示,

上面的對比例子中,Lambda還可以進一步簡化

delegate string DelNumber(string str); //委托型別的回傳型別
DelNumber delNumber2 =  (string str) =>{ return str;}; //Lambda方法
DelNumber delNumber3 =         (str) =>{ return str;}; //省略型別引數
DelNumber delNumber4 =           str =>{ return str;}; //省略型別引數( 如果只有一個隱式型別引數,可以省略周圍的圓括號)
DelNumber delNumber5 =           str =>  str; //陳述句塊替換為return關鍵字后的運算式 ( 如果只有一個回傳陳述句,可以將陳述句塊替換為return關鍵字后的運算式)

如今Lambda運算式已經應用在很多地方了,例如方法體運算式(Expression-Bodied Methods)、自動只讀屬性運算式等等,

Lambda運算式形式上分為兩種:

1.運算式Lambda
當匿名函式只有一行代碼時,可采用這種形式,例如:

DelNumber delNumber= (s4, s5) => s4.Age <= s5.Age;

其中=>符號代表Lambda運算式,它的左側是引數,右側是要回傳或執行的陳述句,引數要放在圓括號中,若只有一個引數,為了方便起見可省略圓括號,有多個引數或者沒有引數時,不可省略圓括號,

相比匿名函式,在運算式Lambda中,方法體的花括號{}和return關鍵字被省略掉了,

用的也是運算式Lambda,這是Lambda運算式的推廣, 是C# 6 編譯器提供的一個語法糖,

2.陳述句Lambda
當匿名函式有多行代碼時,只能采用陳述句Lambda,例如,上面的運算式Lambda可改寫為陳述句Lambda:

DelNumber delNumber= (s4, s5) => 
{
    //此處省略其他代碼
    return s4.Age <= s5.Age;
};

陳述句Lambda不可以省略{}和return陳述句,

完整示例

        delegate string DelNumber(string str); //委托型別的回傳型別
        static void Main(string[] args)
        {

            DelNumber delNumber2 = (string str) => { return str; }; //Lambda方法
            DelNumber delNumber3 = (str) => { return str; }; //省略型別引數
            DelNumber delNumber4 = str => { return str; }; //省略型別引數( 如果只有一個隱式型別引數,可以省略周圍的圓括號)
            DelNumber delNumber5 = str => str; //陳述句塊替換為return關鍵字后的運算式 ( 如果只有一個回傳陳述句,可以將陳述句塊替換為return關鍵字后的運算式)

            Console.WriteLine(delNumber2("lambda"));

            Console.WriteLine(delNumber3("lambda"));
            Console.WriteLine(delNumber4("lambda"));
            Console.WriteLine(delNumber5("lambda"));
            Console.ReadKey();
        }

注意:一個引數可以省略圓括號,多個引數必須圓括號,但是沒有引數,必須使用一組空的圓括號

如: (引數,引數)=>{陳述句} 或者 運算式
       (引數)  =>{陳述句} 或者 運算式
        引數   =>{陳述句} 或者 運算式
        ()    =>{陳述句} 或者 運算式

總結

  1. 委托相當于用方法作為另一方法引數,同時,也可以實作在兩個不能直接呼叫的方法中做橋梁,如在多執行緒中的跨執行緒的方法呼叫就得用委托,
  2. 熟悉在什么情況使用委托,在使用事件設計模式時,當需要封裝靜態方法時,當需要方便的組合時等多種情況下,可以加以使用,
  3. 如果有不對的或不理解的地方,希望大家可以多多指正,提出問題,一起討論,不斷學習,共同進步,
  4. 在下一節中,將對事件進行簡單介紹,并總結歸納,

參考 檔案 《C#圖解教程》

注:搜索關注公眾號【DotNet技術谷】--回復【C#圖解】,可獲取 C#圖解教程檔案

轉載請註明出處,本文鏈接:https://www.uj5u.com/net/20459.html

標籤:C#

上一篇:LeetCode刷題 -- 20200607 前綴和篇

下一篇:【asp.net core】7 實戰之 資料訪問層定義

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • WebAPI簡介

    Web體系結構: 有三個核心:資源(resource),URL(統一資源識別符號)和表示 他們的關系是這樣的:一個資源由一個URL進行標識,HTTP客戶端使用URL定位資源,表示是從資源回傳資料,媒體型別是資源回傳的資料格式。 接下來我們說下HTTP. HTTP協議的系統是一種無狀態的方式,使用請求/ ......

    uj5u.com 2020-09-09 22:07:47 more
  • asp.net core 3.1 入口:Program.cs中的Main函式

    本文分析Program.cs 中Main()函式中代碼的運行順序分析asp.net core程式的啟動,重點不是剖析原始碼,而是理清程式開始時執行的順序。到呼叫了哪些實體,哪些法方。asp.net core 3.1 的程式入口在專案Program.cs檔案里,如下。ususing System; us ......

    uj5u.com 2020-09-09 22:07:49 more
  • asp.net網站作為websocket服務端的應用該如何寫

    最近被websocket的一個問題困擾了很久,有一個需求是在web網站中搭建websocket服務。客戶端通過網頁與服務器建立連接,然后服務器根據ip給客戶端網頁發送資訊。 其實,這個需求并不難,只是剛開始對websocket的內容不太了解。上網搜索了一下,有通過asp.net core 實作的、有 ......

    uj5u.com 2020-09-09 22:08:02 more
  • ASP.NET 開源匯入匯出庫Magicodes.IE Docker中使用

    Magicodes.IE在Docker中使用 更新歷史 2019.02.13 【Nuget】版本更新到2.0.2 【匯入】修復單列匯入的Bug,單元測驗“OneColumnImporter_Test”。問題見(https://github.com/dotnetcore/Magicodes.IE/is ......

    uj5u.com 2020-09-09 22:08:05 more
  • 在webform中使用ajax

    如果你用過Asp.net webform, 說明你也算是.NET 開發的老兵了。WEBform應該是2011 2013左右,當時還用visual studio 2005、 visual studio 2008。后來基本都用的是MVC。 如果是新開發的專案,估計沒人會用webform技術。但是有些舊版 ......

    uj5u.com 2020-09-09 22:08:50 more
  • iis添加asp.net網站,訪問提示:由于擴展配置問題而無法提供您請求的

    今天在iis服務器配置asp.net網站,遇到一個問題,記錄一下: 問題:由于擴展配置問題而無法提供您請求的頁面。如果該頁面是腳本,請添加處理程式。如果應下載檔案,請添加 MIME 映射。 WindowServer2012服務器,添加角色安裝完.netframework和iis之后,運行aspx頁面 ......

    uj5u.com 2020-09-09 22:10:00 more
  • WebAPI-處理架構

    帶著問題去思考,大家好! 問題1:HTTP請求和回傳相應的HTTP回應資訊之間發生了什么? 1:首先是最底層,托管層,位于WebAPI和底層HTTP堆疊之間 2:其次是 訊息處理程式管道層,這里比如日志和快取。OWIN的參考是將訊息處理程式管道的一些功能下移到堆疊下端的OWIN中間件了。 3:控制器處理 ......

    uj5u.com 2020-09-09 22:11:13 more
  • 微信門戶開發框架-使用指導說明書

    微信門戶應用管理系統,采用基于 MVC + Bootstrap + Ajax + Enterprise Library的技術路線,界面層采用Boostrap + Metronic組合的前端框架,資料訪問層支持Oracle、SQLServer、MySQL、PostgreSQL等資料庫。框架以MVC5,... ......

    uj5u.com 2020-09-09 22:15:18 more
  • WebAPI-HTTP編程模型

    帶著問題去思考,大家好!它是什么?它包含什么?它能干什么? 訊息 HTTP編程模型的核心就是訊息抽象,表示為:HttPRequestMessage,HttpResponseMessage.用于客戶端和服務端之間交換請求和回應訊息。 HttpMethod類包含了一組靜態屬性: private stat ......

    uj5u.com 2020-09-09 22:15:23 more
  • 部署WebApi隨筆

    一、跨域 NuGet參考Microsoft.AspNet.WebApi.Cors WebApiConfig.cs中配置: // Web API 配置和服務 config.EnableCors(new EnableCorsAttribute("*", "*", "*")); 二、清除默認回傳XML格式 ......

    uj5u.com 2020-09-09 22:15:48 more
最新发布
  • C#多執行緒學習(二) 如何操縱一個執行緒

    <a href="https://www.cnblogs.com/x-zhi/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/2943582/20220801082530.png" alt="" /></...

    uj5u.com 2023-04-19 09:17:20 more
  • C#多執行緒學習(二) 如何操縱一個執行緒

    C#多執行緒學習(二) 如何操縱一個執行緒 執行緒學習第一篇:C#多執行緒學習(一) 多執行緒的相關概念 下面我們就動手來創建一個執行緒,使用Thread類創建執行緒時,只需提供執行緒入口即可。(執行緒入口使程式知道該讓這個執行緒干什么事) 在C#中,執行緒入口是通過ThreadStart代理(delegate)來提供的 ......

    uj5u.com 2023-04-19 09:16:49 more
  • 記一次 .NET某醫療器械清洗系統 卡死分析

    <a href="https://www.cnblogs.com/huangxincheng/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/214741/20200614104537.png" alt="" /&g...

    uj5u.com 2023-04-18 08:39:04 more
  • 記一次 .NET某醫療器械清洗系統 卡死分析

    一:背景 1. 講故事 前段時間協助訓練營里的一位朋友分析了一個程式卡死的問題,回過頭來看這個案例比較經典,這篇稍微整理一下供后來者少踩坑吧。 二:WinDbg 分析 1. 為什么會卡死 因為是表單程式,理所當然就是看主執行緒此時正在做什么? 可以用 ~0s ; k 看一下便知。 0:000> k # ......

    uj5u.com 2023-04-18 08:33:10 more
  • SignalR, No Connection with that ID,IIS

    <a href="https://www.cnblogs.com/smartstar/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/u36196.jpg" alt="" /></a>...

    uj5u.com 2023-03-30 17:21:52 more
  • 一次對pool的誤用導致的.net頻繁gc的診斷分析

    <a href="https://www.cnblogs.com/dotnet-diagnostic/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/3115652/20230225090434.png" alt=""...

    uj5u.com 2023-03-28 10:15:33 more
  • 一次對pool的誤用導致的.net頻繁gc的診斷分析

    <a href="https://www.cnblogs.com/dotnet-diagnostic/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/3115652/20230225090434.png" alt=""...

    uj5u.com 2023-03-28 10:13:31 more
  • C#遍歷指定檔案夾中所有檔案的3種方法

    <a href="https://www.cnblogs.com/xbhp/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/957602/20230310105611.png" alt="" /></a&...

    uj5u.com 2023-03-27 14:46:55 more
  • C#/VB.NET:如何將PDF轉為PDF/A

    <a href="https://www.cnblogs.com/Carina-baby/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/2859233/20220427162558.png" alt="" />...

    uj5u.com 2023-03-27 14:46:35 more
  • 武裝你的WEBAPI-OData聚合查詢

    <a href="https://www.cnblogs.com/podolski/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/616093/20140323000327.png" alt="" /><...

    uj5u.com 2023-03-27 14:46:16 more