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
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...