那些年困擾我們的委托(C#)

来源:http://www.cnblogs.com/jiekzou/archive/2017/01/09/6262597.html
-Advertisement-
Play Games

委托這個東西不是很好理解,可是工作中又經常用到,你隨處可以看到它的身影,真讓人有一種又愛又恨的感覺,我相信許多人被它所困擾過。 一提到委托,如果你學過C語言,你一定會馬上聯想到函數指針。 什麼是委托?委托是C#中類型安全的,可以訂閱一個或多個具有相同簽名方法的函數指針。委托可以把函數做為參數傳遞,其 ...


委托這個東西不是很好理解,可是工作中又經常用到,你隨處可以看到它的身影,真讓人有一種又愛又恨的感覺,我相信許多人被它所困擾過。

一提到委托,如果你學過C語言,你一定會馬上聯想到函數指針。

什麼是委托?委托是C#中類型安全的,可以訂閱一個或多個具有相同簽名方法的函數指針。委托可以把函數做為參數傳遞,其實際意義便是讓別人代理你的事情。委托可以看做是函數的指針,整數可以用整數變數指向它,對象可以用對象變數指向它,
函數也可以用委托變數指向它。我們可以選擇將委托類型看做只定義了一個方法的介面,而委托的實例可以看做是實現了那個介面的一個對象。

使用委托,必須滿足4個條件:

  • 聲明委托類型;
  • 必須有一個方法包含了要執行的代碼;
  • 必須創建一個委托實例;
  • 必須調用(invoke)委托實例。

委托的申明

聲明委托的方式:delegate 返回值類型 委托類型名(參數)

委托的申明和介面方法的申明基本上一致,只是在返回類型關鍵字的前面多了一個delegate關鍵字。

有如下四種委托:

        //1.無參數無返回值
        public delegate void NoParaNoReturnEventHandler();
        //2.有參數無返回值
        public delegate void WithParaNoReturnEventHandler(string name);
        //3.無參數有返回值
        public delegate string NoParaWithReturnEventHandler();
        //4.有參數有返回值
        public delegate string WithParaWithReturnEventHandler(string name);

如果代碼想要執行操作,但不知道操作細節,一般可以使用委托。例如, Thread類之所以知道要在一個新線程里運行什麼,唯一的原因就是在啟動新線程時,向它提供了一個ThreadStart或ParameterizedThreadStart委托實例。

Thread th = new Thread(Test);
th.Start();

public Thread(ThreadStart start);
public delegate void ThreadStart();

ThreadStart是一個無參無返回值的委托。

        static void Test()
        {
            Console.WriteLine("線程方法");
        }

這個Test方法的函數簽名必須和委托ThreadStart的函數簽名一致。

委托的調用

必須先實例化委托,然後再調用。

函數的簽名和委托的簽名必須一致。NoParaNoReturnEventHandler _NoParaNoReturnEventHandler = ConsoleInfo;,編譯器幫我們進行了new,但是不能寫成NoParaNoReturnEventHandler _NoParaNoReturnEventHandler = ConsoleInfo();

因為這樣就成為了函數調用。

        #region 無返回值委托調用
        public static void Show()
        {
            //實例化委托
            NoParaNoReturnEventHandler _NoParaNoReturnEventHandler = new NoParaNoReturnEventHandler(ConsoleInfo);
            //NoParaNoReturnEventHandler _NoParaNoReturnEventHandler = ConsoleInfo; //簡寫
            //委托調用 通過Invoke()調用,或者可以直接省略
            _NoParaNoReturnEventHandler.Invoke();
            //_NoParaNoReturnEventHandler();

        }
        private static void ConsoleInfo()
        {
            Console.WriteLine("無參數無返回值的函數調用");
        } 
        #endregion

為什麼要使用委托

我們完全可以直接調用方法,為什麼還需要通過一個委托來調用呢?委托有什麼意義?

對修改關閉,對擴展開放。邏輯分離。

你可以把委托理解為函數的父類,或者是一個方法的占位符。

我們來看下代碼,假設有2個方法,一個說英語,一個說漢語,而這2個方法的函數簽名是一樣的。

        public static void SayChinese(string name)
        {
            Console.WriteLine("你好," + name);
        }
        public static void SayEnglish(string name)
        {
            Console.WriteLine("hello," + name);
        }

那麼我們在外部調用的時候,

            MyDelegate.SayChinese("張三");
            MyDelegate.SayEnglish("zhangsan");

如果要調用這兩個不同的方法,是不是要寫不同的調用代碼

我們能不能只一個方法調用呢?修改代碼如下:

        public static void Say(string name,WithParaNoReturnEventHandler handler)
        {
            handler(name);
        }
      public  static void SayChinese(string name)
        {
            Console.WriteLine("你好," + name);
        }
       public  static void SayEnglish(string name)
        {
            Console.WriteLine("hello," + name);
        }

這樣,只通過一個方法Say來進行調用。

如何調用呢?如下三種調用方式:

            WithParaNoReturnEventHandler _WithParaNoReturnEventHandler = new WithParaNoReturnEventHandler(MyDelegate.SayChinese);
            MyDelegate.Say("張三",_WithParaNoReturnEventHandler);
            MyDelegate.Say("張三", delegate(string name) { Console.WriteLine("你好," + name); }); //匿名方法
            MyDelegate.Say("張三", (name) => { Console.WriteLine("你好," + name); }); //lambda表達式

以上代碼使用了幾種調用方式,這些調用方式都是隨著C#的升級而不斷優化的。第一種是C#1.0中就存在的傳統調用方式,第二種是C#2.0中的匿名方法調用方式,所謂匿名方法,就是沒有名字的方法,當方法只調用一次時使用匿名方法最合適不過了。C#3中的lambda表達式。其實泛型委托同樣是被支持的,而.NET 3.5則更進一步,引入了一組名為Func的泛型委托類型,它能獲取多個指定類型的參數,並返回另一個指定類型的值。

也就是說具體調用什麼樣的方法,完全由調用方決定了,就有了更大的靈活性和擴展性。為什麼這麼說,如果我有些時候要先說英語再說漢語,有些事時候要先說漢語再說英語,如果沒有委托,我們會怎麼樣實現?請看如下代碼:

        public static void SayEnglishAndChinese(string name)
        {
            SayEnglish(name);
            SayChinese(name);
        }
        public static void SayChineseAndEnglish(string name)
        {
            SayChinese(name);
            SayEnglish(name);
        }

如果又突然要添加一種俄語呢?被調用方的代碼又要修改,如此迴圈下去,是不是要抓狂了?隨著不斷添加新語種,代碼會變得越來越複雜,越來越難以維護。當然,你可以通過設計模式,在不使用委托的情況下來重構代碼,但是實現起來是非常麻煩的,要寫很多更多的代碼...

委托可以傳遞方法,而這些方法可以代表一系列的操作,這些操作都由調用方來決定,就很好擴展了,而且十分靈活。我們不會對已有的方法進行修改,而是只以添加方法的形式去進行擴展。

可能有人又會說,我直接在調用方那裡來一個一個調用我要執行哪些方法一樣可以實現這樣的效果啊?

可你有沒有想過,你要調用的是一系列方法,你根本無法復用這一系列的方法。使用委托就不一樣了,它好比一個方法集合的容器,你可以往裡面增減方法,可以復用的。而且使用委托,你可以延時方法列表的調用,還可以隨時對方法列表進行增減委托對方法進行了再一次的封裝。

總結:也就是當你只能確定方法的函數簽名,無法確定方法的具體執行時,為了能夠更好的擴展,以類似於註入方法的形式來實現新增的功能,就能體現出委托的價值。

委托和直接調用函數的區別:用委托就可以指向任意的函數,哪怕是之前沒定義的都可以,而不用受限於哪幾種。

多播委托

組合的委托必須是同一個類型,其相當於創建了一個按照組合的順序依次調用的新委托對象。委托的組合一般是給事件用的,用普通委托的時候很少用。

通過+來實現將方法添加到委托實例中,-來從委托實例中進行方法的移除。

+和-純粹是為了簡化代碼而生的,實際上其調用的分別是Delegate.Combine方法和Delegate.Remove。

如果委托中存在多個帶返回值的方法,那麼調用委托的返回值是最後一個方法的返回值。

        public static void MultipleShow()
        {
            //多播委托
            NoParaWithReturnEventHandler _NoParaWithReturnEventHandler = new NoParaWithReturnEventHandler(GetDateTime);
            _NoParaWithReturnEventHandler += GetDateTime;
            Console.WriteLine(_NoParaWithReturnEventHandler());
        }
        public static string GetDateTime()
        {
            return string.Format("今天是{0}號。", DateTime.Now.Day.ToString());
        }

委托總結:

  • 委托封裝了包含特殊返回類型和一組參數的行為,類似包含單一方法的介面;
  • 委托類型聲明中所描述的類型簽名決定了哪個方法可用於創建委托實例,同時決定了調用的簽名;
  • 為了創建委托實例,需要一個方法以及(對於實例方法來說)調用方法的目標;
  • 委托實例是不易變的,就像String一樣;
  • 每個委托實例都包含一個調用列表——一個操作列表;
  • 事件不是委托實例——只是成對的add/remove方法(類似於屬性的取值方法/賦值方法)。

 常見使用場景:窗體傳值、線程啟動時綁定方法等等。

 生活中的例子:現在不是大家都在搶火車票嗎,使用雲搶票就相當於使用委托,你可以直接自己買票,也可以托管於雲搶票,自己搶票的話,在快要開槍的時候,你必須時刻刷新,下單輸驗證碼等等,使用雲搶票的話,你只要放票前,提前輸入搶票信息,就再也不需要你管了,自動出票,你根本不需要知道雲搶票那邊是怎麼幫你實現搶票的。相同時間和車次可以做成一個委托實例,有很多人都通過這個委托實例來進行搶票操作。

源碼下載:http://pan.baidu.com/s/1mic3QjQ


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

-Advertisement-
Play Games
更多相關文章
  • 午休完上班後,同事說測試站點訪問介面出現400 Bad Request Request Header Or Cookie Too Large提示,心想還好是測試伺服器出現問題,影響不大,不過也趕緊上伺服器進行測試查看,打開nginx與ugwsi日誌與配置,發現後端服務日誌記錄正常,而測試站點的訪問日 ...
  • 1、到Apache官網下載tomcat http://tomcat.apache.org/download-80.cgi 博主我下載的是tomcat8 博主的jdk是1.8 如果你們的jdk是1.7或者1.7以下的就下載tomcat6或者tomcat7 2、把下載好的tomcat上傳到Linux虛擬 ...
  • Linux,1991年,系統安全,良好的可移植性,多用戶,多任務,良好的相容性,良好的用戶界面, 主流的是RedHat或者CentOS, CentOS 設置的網關 192.168.2.2 Windows 設置的網關 192.168.2.1 取消命令行一直運行:Ctrl+c 在命令行中切換root用戶 ...
  • 本節內容 1.github介紹 很多人都知道,Linus在1991年創建了開源的Linux,從此,Linux系統不斷發展,已經成為最大的伺服器系統軟體了。 Linus雖然創建了Linux,但Linux的壯大是靠全世界熱心的志願者參與的,這麼多人在世界各地為Linux編寫代碼,那Linux的代碼是如何 ...
  • 觀察系統當前進程的運行情況的命令是( ):A、freeB、dmesgC、topD、last 答案:http://hovertree.com/tiku/bjag/foxg5n0q.htm Linux系統通過( )命令給其他用戶發消息?A.lessB.mesg yC.writeD.echo to 答案: ...
  • nginx工作模式-->1個master+n個worker進程 安裝nginx的所需pcre庫【用於支持rewrite模塊】 下載軟體方法: 搜索 pcre download 網址:http://pcre.org 下載pcre包 wget ftp://ftp.csx.cam.ac.uk/pub/so ...
  • 1、在/etc/init.d/目錄下創建 nginx 文件,內容如下: 2.設置/etc/init.d/nginx 執行許可權 3.設置開機預設啟動 5.nginx控制命令 ...
  • 曾經六六我也是一個初級開發的實習生,當年的我初入農田,一心要做一個高產量高品質的碼農,做碼農界的袁隆平!然而,現實總是無比的殘酷,我一個剛入門的碼農,剛進農田就跌了許多跟頭。BUG天天困擾著我,不過這也是一種磨練。不然我六六也難以成為一個合格的碼農! 不過現在時代不同了,軟體公司越來越多,軟體開發行 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...