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

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

委托的定義 什麼是委托? 委托實際上是一種類型,是一種引用類型。 微軟用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類中被觸發了。

運行結果如下:

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

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

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

重要的話要講三遍。

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

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

結語

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

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

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

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

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

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

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

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


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

-Advertisement-
Play Games
更多相關文章
一周排行
    -Advertisement-
    Play Games
  • 概述:在C#中,++i和i++都是自增運算符,其中++i先增加值再返回,而i++先返回值再增加。應用場景根據需求選擇,首碼適合先增後用,尾碼適合先用後增。詳細示例提供清晰的代碼演示這兩者的操作時機和實際應用。 在C#中,++i 和 i++ 都是自增運算符,但它們在操作上有細微的差異,主要體現在操作的 ...
  • 上次發佈了:Taurus.MVC 性能壓力測試(ap 壓測 和 linux 下wrk 壓測):.NET Core 版本,今天計劃準備壓測一下 .NET 版本,來測試並記錄一下 Taurus.MVC 框架在 .NET 版本的性能,以便後續持續優化改進。 為了方便對比,本文章的電腦環境和測試思路,儘量和... ...
  • .NET WebAPI作為一種構建RESTful服務的強大工具,為開發者提供了便捷的方式來定義、處理HTTP請求並返迴響應。在設計API介面時,正確地接收和解析客戶端發送的數據至關重要。.NET WebAPI提供了一系列特性,如[FromRoute]、[FromQuery]和[FromBody],用 ...
  • 原因:我之所以想做這個項目,是因為在之前查找關於C#/WPF相關資料時,我發現講解圖像濾鏡的資源非常稀缺。此外,我註意到許多現有的開源庫主要基於CPU進行圖像渲染。這種方式在處理大量圖像時,會導致CPU的渲染負擔過重。因此,我將在下文中介紹如何通過GPU渲染來有效實現圖像的各種濾鏡效果。 生成的效果 ...
  • 引言 上一章我們介紹了在xUnit單元測試中用xUnit.DependencyInject來使用依賴註入,上一章我們的Sample.Repository倉儲層有一個批量註入的介面沒有做單元測試,今天用這個示例來演示一下如何用Bogus創建模擬數據 ,和 EFCore 的種子數據生成 Bogus 的優 ...
  • 一、前言 在自己的項目中,涉及到實時心率曲線的繪製,項目上的曲線繪製,一般很難找到能直接用的第三方庫,而且有些還是定製化的功能,所以還是自己繪製比較方便。很多人一聽到自己畫就害怕,感覺很難,今天就分享一個完整的實時心率數據繪製心率曲線圖的例子;之前的博客也分享給DrawingVisual繪製曲線的方 ...
  • 如果你在自定義的 Main 方法中直接使用 App 類並啟動應用程式,但發現 App.xaml 中定義的資源沒有被正確載入,那麼問題可能在於如何正確配置 App.xaml 與你的 App 類的交互。 確保 App.xaml 文件中的 x:Class 屬性正確指向你的 App 類。這樣,當你創建 Ap ...
  • 一:背景 1. 講故事 上個月有個朋友在微信上找到我,說他們的軟體在客戶那邊隔幾天就要崩潰一次,一直都沒有找到原因,讓我幫忙看下怎麼回事,確實工控類的軟體環境複雜難搞,朋友手上有一個崩潰的dump,剛好丟給我來分析一下。 二:WinDbg分析 1. 程式為什麼會崩潰 windbg 有一個厲害之處在於 ...
  • 前言 .NET生態中有許多依賴註入容器。在大多數情況下,微軟提供的內置容器在易用性和性能方面都非常優秀。外加ASP.NET Core預設使用內置容器,使用很方便。 但是筆者在使用中一直有一個頭疼的問題:服務工廠無法提供請求的服務類型相關的信息。這在一般情況下並沒有影響,但是內置容器支持註冊開放泛型服 ...
  • 一、前言 在項目開發過程中,DataGrid是經常使用到的一個數據展示控制項,而通常表格的最後一列是作為操作列存在,比如會有編輯、刪除等功能按鈕。但WPF的原始DataGrid中,預設只支持固定左側列,這跟大家習慣性操作列放最後不符,今天就來介紹一種簡單的方式實現固定右側列。(這裡的實現方式參考的大佬 ...