8.C#知識點:委托和事件

来源:https://www.cnblogs.com/DingKing/archive/2017/12/28/8137521.html
-Advertisement-
Play Games

知識點目錄 >傳送門 首先推薦兩篇大牛寫的委托和事件的博客,寫的超級好!看了就包你看會,想學習的朋友直接看這兩篇就足以,我自己寫的是算是自己學習的紀錄。 傳送門 》C# 中的委托和事件 C# 中的委托和事件續。 委托是什麼? 委托是一個類,它定義了一種的類型,使得可以將方法當作另一個方法的參數來進行 ...


知識點目錄==========>傳送門

首先推薦兩篇大牛寫的委托和事件的博客,寫的超級好!看了就包你看會,想學習的朋友直接看這兩篇就足以,我自己寫的是算是自己學習的紀錄。

    傳送門==========》C# 中的委托和事件

                    C# 中的委托和事件續

 

委托是什麼?

委托是一個類,它定義了一種的類型,使得可以將方法當作另一個方法的參數來進行傳遞,這種將方法動態地賦給參數的做法,可以避免在程式中大量使用If-Else(Switch)語句,同時使得程式具有更好的可擴展性。

--摘自百度百科。

說白了委托和我們平常常見的類是差不多的東西。它也是一個類型,一個對象。委托定義類似定義一種方法模板。滿足於這個模板的任何方法都可以賦值於委托。並且將這個委托當參數進行傳遞,進而把方法當參數傳遞。

    public delegate void deTest(string name);

這就定義個沒有返回值的委托。定義委托需要關鍵字 delegate,這個關鍵字和我們定義的class關鍵字是一樣的,記住是這樣定義的就好了,下麵部分就和定義方法的聲明是一樣的。去掉delegate關鍵,就是和定義方法一模一樣。

只要滿足沒有返回值,而且參數是string類型的參數的方法都滿足於這個委托。都可以賦值綁定給這個委托。還是上面那句話.定義委托就等於聲明瞭一個方法的模板。

接下來就演示下委托如何使用

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace 委托和事件
{
    public delegate void deTest(string name);
    public class Test
    {
        public void SayHello(string name, deTest test)
        {
            test(name);
        }
    }
}

首先看委托是定義在類外面的,說明定義類的地方都可以定義委托,委托和類是平級關係的。

Test類裡面有個SayHello方法。這個方法有兩個參數。一個就是string類型的參數,一個是委托類型的參數。委托類型的參數是什麼意思呢?意思就是將滿足委托模板的方法,將方法當作參數傳遞。因為沒有委托方法是無非當參數傳遞的,最後SayHello方法裡面調用這個委托。因為這個委托參數本身也是方法。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace 委托和事件
{

    class Program
    {
        static void Main(string[] args)
        {

            Test t1 = new Test();
            deTest detest = SayHelloByEngilsh;
            t1.SayHello("小明",detest);
        }

        public static void SayHelloByEngilsh(string name)
        {
            Console.WriteLine(string.Format("Hello{0}", name));
        }
    }
}

首先我們有一個SayHelloByEnglish的方法。這個方法正好滿足我們這個委托模板,無返回值,有一個string類型的參數。Main函數里將這個方法賦值於我們定義的委托。然後將這個委托傳遞到我們test的SayHello方法裡面。

這個例子並不適合使用委托,不是一個好的使用委托的場景,在這裡面寫主要是為了大家瞭解委托如何創建使用。現在我們在重新梳理一下。首先我們定義了一個Test類,在類裡面有個SayHello方法。這個方法有兩個參數對吧。

一個是string類型,一個是委托類型。方法面執行這個委托。因為委托本身就是綁定的方法,值就是方法,所以可以讓方法直接調用。接下來是main函數裡面真正的調用。將一個滿足委托的方法賦值於委托,然後傳遞給了這個方法。最後方法內部執行的委托的時候,其實是執行了我們綁定的方法。也就是SayHelloTest。一個委托創建使用的demo就結束了。

總結下委托

委托賦值語發是+

綁定語法是+=

解綁語法是-=

下麵來演示下委托的應用場景,現在我們有個需求,有一個熱水器,熱水器連接著顯示器,和報警器。熱水器溫度達到80°的時候,顯示器就會顯示提示,報警器就會報警。先用正常方法實現下。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace 委托和事件
{
    public class WaterHeater
    {
        int temperature = 0;
        public void BoilWater()
        {
            for (int i = 0; i < 80; i++)
            {
                temperature++;
            }
            //報警器報警
            //顯示器報警
        }
    }
}

這裡就定義個熱水器類,這個類就是一個燒水的類。類裡面溫度一直再加,到了80就開始報警了。報警的操作占時沒寫。下麵就開定義報警類。和顯示器類。

    public class Monitor
    {
        public void ShowMessage(int temperature)
        {
            Console.WriteLine(string.Format("熱水器在的溫度已經{0}°啦,趕緊去洗澡吧", temperature));
        }
    }
    public class Alarm
    {
        public void Police(int temperature)
        {
            Console.WriteLine(string.Format("熱水器在的溫度已經{0}°啦,趕緊去洗澡吧", temperature));
        }
    }

這個就是顯示器類和燒水類。兩個類的東西一樣,只有一個簡單的報警方法。接下來就是將熱水器到了80°之後的報警操作補全。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace 委托和事件
{
    public class WaterHeater
    {
        int temperature = 0;
        public void BoilWater()
        {
            for (int i = 0; i < 80; i++)
            {
                temperature++;
            }
            //報警器報警
            Monitor monitor = new Monitor();
            Alarm alarm = new Alarm();
            monitor.ShowMessage(temperature);
            alarm.Police(temperature);//顯示器報警
  } }
}

這地方就是new了報警器和顯示器的對象。然後調用兩個對象的報警方法。功能已經實現了。但是這個地方有問題。寫的很low,看到兩個new我們就應該知道這個地方又是強耦合。不利於需求變動。萬一哪天我們新加了一個別的報警裝置。我還們還要修改這個熱水器燒水的方法。設計的很不好。大家想想這個地方無非是要在溫度到了80°之後執行顯示器和報警器裡面的報警方法。我們剛剛講過的委托就是可以和方法綁定。那這個地方我們可不可以讓熱水器暴露一個委托變數,熱水器溫度達到80°之後。調用這個委托變數。委托的綁定放在外面。這樣的話。就將高耦合變成了沒有耦合,或者說是低耦合。如果沒有明白的話。看一手代碼。

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace 委托和事件
{
  //定義委托
public delegate void Prompt(int temperature); public class WaterHeater {
    //聲明委托變數
public Prompt promt; public int temperature = 0; public void BoilWater() { for (int i = 0; i < 80; i++) { temperature++; }
promt(temperature); } }
}

這個地方我們定義了一個委托,修改WaterHeater類聲明瞭一個這個委托的變數。這個地方我改為調用這個委托變數用來取代原來的與報警器和顯示器的關聯,將之前的高耦合進行解耦。現在這個類已經和報警器沒有一點關聯。但是不能沒有一點關聯的對吧。不然怎麼調用是吧。接下演示綁定的地方。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace 委托和事件
{

    class Program
    {
        static void Main(string[] args)
        {
            Monitor monitor = new Monitor();
            Alarm alarm = new Alarm();
            WaterHeater waterHeater = new WaterHeater();
            waterHeater.promt = monitor.ShowMessage;//委托綁定
            waterHeater.promt += alarm.Police;//委托綁定
            waterHeater.BoilWater();
        }

    }
}

我們在main方法裡面進行對委托的綁定。我們將之前在類的內部的強耦合提到了外面。如果不需要某個報警裝置了,或者我們要添加某個裝置。只要在main方法對委托進行綁定,或者解綁就好了。不需要再去修改熱水器燒水的代碼了。到這裡委托的基本使用就介紹完了,下麵介紹下事件。

事件是什麼? 事件就是對委托的封裝。舉個例子,屬性封裝了欄位。事件就是等於對委托的封裝。有了事件我賦值可以直接使用+=或者-=而不需要用+。

下麵演示一波事件代碼。將之前的代碼進行修改。

namespace 委托和事件
{
    public delegate void Prompt(int temperature);
    public class WaterHeater
    {
        private Prompt promt;
        public event Prompt OnPromptEvent;//定義Prompt委托類型的事件。
        public int temperature = 0;
        public void BoilWater()
        {
            for (int i = 0; i < 80; i++)
            {
                temperature++;
            }
        //調用事件 OnPromptEvent(temperature); } }

這個地方我們又聲明瞭一個事件。並且將調用委托的地方改為了事件,還將委托的訪問類型改為私有。main方法我們也進行了修改。

事件小總結

1.聲明關鍵字 event

2.關鍵字後面緊跟委托類型

修改main方法

        static void Main(string[] args)
        {
            Monitor monitor = new Monitor();
            Alarm alarm = new Alarm();
            WaterHeater waterHeater = new WaterHeater();
            waterHeater.OnPromptEvent += monitor.ShowMessage;
            waterHeater.OnPromptEvent += alarm.Police;
            waterHeater.BoilWater();
        }

將委托的綁定改為了事件綁定。到了這裡有沒有同學發現和我們winform 按鈕事件是極為相似的,其實按鈕那個點擊事件也是這個事件。

   this.button1.Click += new System.EventHandler(this.button1_Click);


   private void button1_Click(object sender, EventArgs e)
   {

   }

這是Vs幫我們生成的按鈕綁定事件,是不是和我們寫的一樣。但是細心的小朋友可能會說:參數定義不一樣,Vs裡面的事件都會有(object sender, EventArgs e)這兩個參數。

其實這是微軟定義的一種委托事件聲明的規範。sender 傳遞觸發者,EventArgs參數傳遞而外信息。比如我們寫的那個demo sender就是熱水器本身,參數我們就要傳遞的溫度。下麵我來修改一下。

首先添加一個參數類繼承於EventArgs我們自定義的要傳遞的參數都繼承於EventArgs。篇幅有限制,不詳細介紹。可以自己查閱資料或者看我文章看透推薦的博客。

 

    public class BoiledEventArgs : EventArgs
    {
        public readonly int temperature;
        public BoiledEventArgs(int temperature)
        {
            this.temperature = temperature;
        }
    }

修改了顯示器類和報警器類的方法簽名

    public class Monitor
    {
        public void ShowMessage(object sender, BoiledEventArgs e)
        {
            Console.WriteLine(string.Format("熱水器在的溫度已經{0}°啦,趕緊去洗澡吧", e.temperature));
        }
    }
    public class Alarm
    {
        public void Police(object sender, BoiledEventArgs e)
        {
            Console.WriteLine(string.Format("熱水器在的溫度已經{0}°啦,趕緊去洗澡吧", e.temperature));
        }
    }

修改熱水器類

    public delegate void Prompt(object sender, BoiledEventArgs e);
    public class WaterHeater
    {
        private Prompt promt;

        //定義Prompt類型事件
        public event Prompt OnPromptEvent;
        public int temperature = 0;
        public void BoilWater()
        {
            for (int i = 0; i < 80; i++)
            {
                temperature++;
            }
            BoiledEventArgs e = new BoiledEventArgs(temperature);
            OnPromptEvent(this, e);
        }
    }

main方法是不用動的。

完整代碼:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace 委托和事件
{
    public delegate void Prompt(object sender, BoiledEventArgs e);
    public class WaterHeater
    {
        private Prompt promt;

        //定義Prompt類型事件
        public event Prompt OnPromptEvent;
        public int temperature = 0;
        public void BoilWater()
        {
            for (int i = 0; i < 80; i++)
            {
                temperature++;
            }
            BoiledEventArgs e = new BoiledEventArgs(temperature);
            OnPromptEvent(this, e);
        }
    }

    // 定義BoiledEventArgs類,傳遞需要的的信息
    public class BoiledEventArgs : EventArgs
    {
        public readonly int temperature;
        public BoiledEventArgs(int temperature)
        {
            this.temperature = temperature;
        }
    }


    public class Monitor
    {
        public void ShowMessage(object sender, BoiledEventArgs e)
        {
            Console.WriteLine(string.Format("熱水器在的溫度已經{0}°啦,趕緊去洗澡吧", e.temperature));
        }
    }
    public class Alarm
    {
        public void Police(object sender, BoiledEventArgs e)
        {
            Console.WriteLine(string.Format("熱水器在的溫度已經{0}°啦,趕緊去洗澡吧", e.temperature));
        }
    }
}
View Code

講到了。我就介紹完啦。最後有一點倉促收尾。哈哈。想要學習的小伙伴可以看我文章推薦的兩個博客啦。目前見過寫的最好的了。我這個主要是自己紀錄學習下。寫下來理解更深刻,印象更勝。不適合大家學習。純屬個人筆記。


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

-Advertisement-
Play Games
更多相關文章
  • 揭短ExceptionUtils:有些異常並沒有root cause的,此時,調用ExceptionUtils的getRootCause(final Throwable throwable)返回值是null,而你調用其getRootCauseMessage(final Throwable th)時,... ...
  • 前一篇博客講述了Spring的一些基礎概念,下麵我們來創建第一個Spring程式吧。 步驟如下: 1) 導包 2) 配置文件 附沒有提示的情況 MyEclipse ->File and Editors -XML ->XML Catlog, 點擊 add,找到上面的文件 spring-beans-3. ...
  • 1 list = ((1,'iPhone X',8300),(2,'iPad Pro',4600),(3,'IBM z10',50000),(4,'Coffee',30)) 2 print('Welecome to 7-11 !\n') 3 print('There are:') 4 for i i... ...
  • 本篇導航 發送郵件 發送微信 一、發送郵件 1、實現發送郵件腳本 2、常見SMTP服務 3、封裝對象 二、發送微信 發送微信必須是微信公眾號不能直接給微信發送消息 微信公眾平臺介面測試帳號申請:https://mp.weixin.qq.com/debug/cgi-bin/sandboxinfo?ac ...
  • 工廠模式:主要用來實例化有共同介面的類,工廠模式可以動態決定應該實例化那一個類。 工廠模式主要有: 簡單工廠模式,工廠方法,抽象工廠; 簡單工廠: 又叫靜態工廠,是工廠模式三中狀態中結構最為簡單的。主要有一個靜態方法,用來接受參數,並根據參數來決定返回實現同一介面的不同類的實例。我們來看一個具體的例 ...
  • class int(object) | int(x=0) -> integer | int(x, base=10) -> integer | | Convert a number or string to an integer, or return 0 if no arguments | are g ...
  • 反射reflection有一個類別PropertyInfo,是獲取一個類別的特性相關集息。下麵我創建一個類,並添加一個特性,然後我們使用反射來為這個特性賦值。 class Bq { public string Name { get; set; } } 接下來,我們創建另一個類,來實現反射: clas ...
  • 可以把字元串轉換為MemoryStream。也可以把MenoryStream轉換為字元串。 下麵Insus.NET寫了幾個方法: class Bq { public string Input { get; set; } public byte[] Byte { get; set; } public ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...