c#url拼接方法名_C# 從1到Core委托與事件

委托與事件在C#1.0的時候就有了,隨著C#版本的不斷更新,有些寫法和功能也在不斷改變。本文溫故一下這些改變,以及在NET Core中關于事件的一點改變。

一、C#1.0?從委托開始

1. 基本方式

  什么是委托,就不說概念了,用例子說話。

  某HR說他需要招聘一個6年 .NET5 研發經驗的“高級”工程師,他想找人(委托)別人把這條招聘消息發出去。這樣的HR很多,所以大家定義了一個通用的發消息規則:

public delegate string SendDelegate(string message);

  這就像一個接口的方法,沒有實際的實現代碼,只是定義了這個方法有一個string的參數和返回值。所有想發招聘消息的HR只要遵守這樣的規則即可。

委托本質上是一個類,所以它可以被定義在其他類的內部或外部,根據實際引用關系考慮即可。本例單獨定義在外部。

為HR定義了一個名為HR的類:

public class HR{    public SendDelegate sendDelegate;    public void SendMessage(string msg)    {        sendDelegate(msg);    }}

  HR有一個SendDelegate類型的成員,當它需要發送消息(SendMessage)的時候,只需要調用這個sendDelegate方法即可。而不需要實現這個方法,也不需要關心這個方法是怎么實現的。

當知道這個HR需要發送消息的時候,獵頭張三接了這個幫忙招人的工作。獵頭的類為Sender,他有一個用于發送消息的方法Send,該方法恰好符合眾人定義的名為SendDelegate的發消息規則。這有點像實現了一個接口方法,但這里不要求方法名一致,只是要求方法的簽名一致。

public class Sender{    public Sender(string name)    {        this.senderName = name;    }    private readonly string senderName;    public string Send(string message)    {        string serialNumber = Guid.NewGuid().ToString();        Console.WriteLine(senderName + " sending....");        Thread.Sleep(2000);        Console.WriteLine("Sender: " + senderName + " , Content: " + message + ", Serial Number: "  + serialNumber);        return serialNumber;    }}

獵頭幫助HR招人的邏輯如下:

public void Test(){    //一個HR    HR hr = new HR();    //獵頭張三來監聽,聽到HR發什么消息后立刻傳播出去    Sender senderZS = new Sender("張三");    hr.sendDelegate = senderZS.Send;    //HR遞交消息    hr.SendMessage("Hello World");}

獵頭將自己的發消息方法“賦值”給了HR的SendDelegate方法,為什么可以“賦值”? 因為二者都遵守SendDelegate規則。 就像A和B兩個變量都是int類型的時候,A可以賦值給B一樣。

這就是一個簡單的委托過程,HR將招人的工作委托給了獵頭,自己不用去做招人的工作。

但經常一個招聘工作經常會有多個獵頭接單,那就有了多播委托。

2. 多播委托

?看一下下面的代碼:

public void Test(){    //一個HR    HR hr = new HR();    //獵頭張三來監聽,聽到HR發什么消息后立刻傳播出去    Sender senderZS = new Sender("張三");    hr.sendDelegate = senderZS.Send;    //快嘴李四也來了    Sender senderLS = new Sender("李四");    hr.sendDelegate += senderLS.Send;    //HR遞交消息    hr.SendMessage("Hello World");}

與之前的代碼改變不大, 只是添加了李四的方法綁定,這樣HR發消息的時候,張三和李四都會發出招人的消息。

這里要注意李四綁定方法的時候,用的是+=而不是=,就像拼接字符串一樣,是拼接而不是賦值,否則會覆蓋掉之前張三的方法綁定。

對于第一個綁定的張三,可以用=號也可以用+=(記得之前好像第一個必須用=,實驗了一下現在二者皆可)。

這同時也暴露了一些問題:

  • 如果后面的獵頭接單的時候不小心(故意)用了=號, 那么最終前面的人的綁定都沒有了,那么他將獨占這個HR客戶,HR發出的消息只有他能收到。

  • 可以偷偷的調用獵頭的hr.sendDelegate

public void Test(){    //一個HR    HR hr = new HR();    //大嘴張三來監聽,聽到HR發什么消息后立刻傳播出去    Sender senderZS = new Sender("張三");    //hr.sendDelegate -= senderZS.Send; //即使未進行過+=  直接調用-=,也不會報錯    hr.sendDelegate += senderZS.Send;    //快嘴李四也來了    Sender senderLS = new Sender("李四");    hr.sendDelegate += senderLS.Send;    //移除    //hr.sendDelegate -= senderZS.Send;    //風險:注意上面用的符號是+=和-=   如果使用=,則是賦值操作,    //例如下面的語句會覆蓋掉之前所有的綁定    //hr.sendDelegate = senderWW.Send;    //HR遞交消息    hr.SendMessage("Hello World");    //風險:可以偷偷的以HR的名義偷偷的發了一條消息    sendDelegate應該只能由HR調用       hr.sendDelegate("偷偷的發一條");}

3. 通過方法避免風險

  很自然想到采用類似Get和Set的方式避免上面的問題。既然委托可以像變量一樣賦值,那么也可以通過參數來傳值,將一個方法作為參數傳遞。

public class HRWithAddRemove    {        private SendDelegate sendDelegate;        public void AddDelegate(SendDelegate sendDelegate)        {            this.sendDelegate += sendDelegate; //如果需要限制最多綁定一個,此處可以用=號        }        public void RomoveDelegate(SendDelegate sendDelegate)        {            this.sendDelegate -= sendDelegate;        }        public void SendMessage(string msg)        {            sendDelegate(msg);        }    }

經過改造后的HR,SendDelegate方法被設置為了private,之后只能通過Add和Remove的方法進行方法綁定。

4.模擬多播委托機制

通過上面委托的表現來看,委托就像是保存了一個相同方法名的集合?List?,可以向集合中添加或移除方法,當調用這個委托的時候,會逐一調用該集合中的各個方法。

例如下面的代碼( 注意這里假設SendDelegate只對應一個方法?):

public class HR1{    public void Delegate(SendDelegate sendDelegate)    {        sendDelegateList = new List { sendDelegate }; //對應=    }    public void AddDelegate(SendDelegate sendDelegate)    {        sendDelegateList.Add(sendDelegate); //對應+=    }    public void RomoveDelegate(SendDelegate sendDelegate)    {        sendDelegateList.Remove(sendDelegate);//對應-=    }    public List sendDelegateList;    public void SendMessage(string msg)    {        foreach (var item in sendDelegateList)        {            item(msg);        }    }}

二、C#1.0?引入事件

  1.簡單事件

  如果既想使用-=和+=的方便,又想避免相關功能開閉的風險怎么辦呢?可以使用事件:

public class HRWithEvent    {        public event SendDelegate sendDelegate;        public void SendMessage(string msg)        {            sendDelegate(msg);        }    }

 只是將SendDelegate前面添加了一個event標識,雖然它被設置為public,但如下代碼卻會給出錯誤提示:?事件“HRWithEvent.sendDelegate”只能出現在 += 或 -= 的左邊(從類型“HRWithEvent”中使用時除外)?

 hr.sendDelegate = senderZS.Send; hr.sendDelegate("偷偷的發一條");

  2.事件的訪問器模式

?  上文為委托定義了Add和Remove方法,而事件支持這樣的訪問器模式,例如如下代碼:

public class CustomerWithEventAddRemove    {        private event SendDelegate sendDelegate;        public event SendDelegate SendDelegate        {            add { sendDelegate += value; }            remove { sendDelegate -= value; }        }        public void SendMessage(string msg)        {            sendDelegate(msg);        }    }

 可以像使用Get和Set方法一樣,對事件的綁定與移除進行條件約束。?

  3. 控制綁定事件的執行

  當多個委托被綁定到事件之后,如果想精確控制各個委托的運行怎么辦,比如返回值(雖然經常為void)、異常處理等。

第一章第4節通過一個List 模擬了多播委托的綁定。 會想到如果真能循環調用一個個已綁定的委托,就可以精確的進行控制了。那么這里說一下這樣的方法:

public class HRWithEvent    {        public event SendDelegate sendDelegate;        public void SendMessage(string msg)        {            //sendDelegate(msg);  此處不再一次性調用所有            if (sendDelegate != null)            {                Delegate[] delegates = sendDelegate.GetInvocationList(); //獲取所有已綁定的委托                foreach (var item in delegates)                {                    ((SendDelegate)item).Invoke(msg); //逐一調用                }            }        }    }

 這里通過Invoke方法逐一調用各個Delegate,從而實現對每一個Delegate的調用的控制。若需要異步調用,則可以通過BeginInvoke方法實現(.NET Core之后不再支持此方法,后面會介紹。)

((SendDelegate)item).BeginInvoke(msg,null,null);

  4. 標準的事件寫法

  .NET 事件委托的標準簽名是:

void OnEventRaised(object sender, EventArgs args);

  返回類型為 void。?事件基于委托,而且是多播委托。?參數列表包含兩種參數:發件人和事件參數。?sender?的編譯時類型為?System.Object

  第二種參數通常是派生自?System.EventArgs?的類型(.NET Core 中已不強制要求繼承自System.EventArgs,后面會說到)

  將上面的例子修改一下,改成標準寫法,大概是下面代碼的樣子:

public class HRWithEventStandard{    public delegate void SendEventHandler(object sender, SendMsgArgs e);    public event SendEventHandler Send;    public void SendMessage(string msg)    {        var arg = new SendMsgArgs(msg);        Send(this,arg); //arg.CancelRequested 為最后一個的值   因為覆蓋    }}public class SendMsgArgs : EventArgs{    public readonly string Msg = string.Empty;    public bool CancelRequested { get; set; }    public SendMsgArgs(string msg)    {        this.Msg = msg;    }}

三、隨著C#版本改變

1. C#2.0 泛型委托

  C#2.0?的時候,隨著泛型出現,支持了泛型委托,例如,在委托的簽名中可以使用泛型,例如下面代碼

public delegate string SendDelegate(T message);

這樣的委托適用于不同的參數類型,例如如下代碼(注意使用的時候要對應具體的類型)

public delegate string SendDelegate(T message);public class HR1{    public SendDelegate<string> sendDelegate1;    public SendDelegate<int> sendDelegate2;    public SendDelegate sendDelegate3;}public static class Sender1{    public static string Send1(string msg)    {        return "";    }    public static string Send2(int msg)    {        return "";    }}    public class Test{    public void TestDemo()    {        HR1 hr1 = new HR1();        hr1.sendDelegate1 = Sender1.Send1; // 注意使用的時候要對應具體的類型        hr1.sendDelegate2 = new SendDelegate<int>(Sender1.Send2);        hr1.sendDelegate3 = delegate (DateTime dateTime) { return dateTime.ToLongDateString(); };    }}

2. C#2.0 delegate運算符

delegate?運算符創建一個可以轉換為委托類型的匿名方法:

例如上例中這樣的代碼:

hr1.sendDelegate3 = delegate (DateTime dateTime) { return dateTime.ToLongDateString(); };

3. C#3.0?Lambda 表達式

從 C# 3 開始,lambda 表達式提供了一種更簡潔和富有表現力的方式來創建匿名函數。?使用?=> 運算符構造 lambda 表達式,

例如“delegate運算符”的例子可以簡化為如下代碼:

hr1.sendDelegate3 = (dateTime) => { return dateTime.ToLongDateString(); };

4.C#3,NET Framework3.5,Action 、Func、Predicate

Action 、Func、Predicate本質上是框架為我們預定義的委托,在上面的例子中,我們使用委托的時候,首先要定義一個委托類型,然后在實際使用的地方使用,而使用委托只要求方法名相同,在泛型委托出現之后,“定義委托”這一操作就顯得越來越累贅,為此,系統為我們預定義了一系列的委托,我們只要使用即可。

例如Action的代碼如下:

df5974d402a4433624ed1a54c09ecc66.png

實際上定義了最多16個參數的無返回值的委托。

Func與此類似,是最多16個參數的有返回值的委托。Predicate則是固定一個參數以及bool類型返回值的委托。

public delegate bool Predicate(T obj);

?5. .NET Core?異步調用

第2.3節中,提示如下代碼在.NET Core中已不支持

((SendDelegate)item).BeginInvoke(msg,null,null);

會拋出異常:

System.PlatformNotSupportedException:“Operation is not supported on this platform.”

需要異步調用的時候可以采用如下寫法:

Task task = Task.Run(() => ((SendDelegate)item).Invoke(msg));

對應的?EndInvoke()?則改為:?task.Wait();?

?5. .NET Core的 EventHandler

.NET Core 版本中,EventHandler?定義不再要求?TEventArgs?必須是派生自?System.EventArgs?的類, 使我們使用起來更為靈活。

例如我們可以有這樣的寫法:

EventHandler<string> SendNew

?這在以前的版本中是不允許的。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/443114.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/443114.shtml
英文地址,請注明出處:http://en.pswp.cn/news/443114.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

linux禁止客戶端上傳文件_Linux下Shell腳本實現FTP自動上傳和下載文件

概述之前測試寫的一個腳本&#xff0c;主要是為了實現FTP自動上傳和下載文件。大家可以參考下&#xff1a;基礎環境配置[rootpxzxdm01 ~]# useradd -d /home/ftp1 ftp1 [rootpxzxdm01 ~]# passwd ftp1 (密碼123456)[rootpxzxdm01 ~]# usermod -s /sbin/nologin ftp1 (限定用戶f…

谷歌瀏覽器打開tif_谷歌打開后是搜狗瀏覽器??怎么辦

方法如下&#xff1a;右鍵點開谷歌圖標—屬性—文件所在位置—找到谷歌程序—右鍵建立新的快捷方式—打開新的快捷谷歌&#xff0c;好了谷歌設置里面的啟動 刪除搜狗瀏覽器的方法&#xff0c;親試沒用&#xff0c;如有其他方法&#xff0c;歡迎交流

python安裝requests模塊失敗_No module named quot;Cryptoquot;,如何安裝Python三方模塊Crypto...

前兩天公司公司老總讓我研究怎么用企業微信第三方應用進行官網對接&#xff0c;完成URL回調驗證問題。具體如何進行Python 的Django網站與企業微信第三方應用進行回調驗證的博客地址為&#xff1a;https://www.cnblogs.com/ws17345067708/p/10522472.html這里講講&#xff0c;如…

對數函數定義域和值域為r_對數函數

我們知道&#xff0c;指數函數 &#xff0c;對于每一個確定值x&#xff0c;都有一個y值與它相對應。并且當x取不同值時&#xff0c;得到的函數值y也是不同的。也就是說指數函數的自變量與因變量是一一對應的。對于任意的,在R中都有唯一的數x滿足 。如果把y看做自變量&#xff0…

怎樣測量地圖上曲線的長度_測量長度的特殊方法

小可愛們&#xff0c;大家好啊~在這里先做個自我介紹吧&#xff01;我是五色鳥的物理講師&#xff0c;欣欣老師~以后的每周物理&#xff0c;就由我來為大家分享啦~~快&#xff0c;呱唧呱唧&#xff01;&#xff01;今天我們來一起學習一些特殊的測量方法&#xff01;&#xff0…

32通過rs485燒寫程序_28027芯片sci程序燒寫問題請教

需求&#xff1a;希望用TI官方提供的“f28027_flash_kernel”過通sci串口燒錄程序如下所示bootload與pc燒錄軟件都是TI官方提供的。pc燒錄軟件: D:ticontrolSUITEdevice_support~Utilitiesserial_flash_programmer f28027_flash_kernel&#xff1a;D:ticontrolSUITEdevice_supp…

python缺少標準庫_Python 實現自動導入缺失的庫

作者&#xff1a;豌豆花下貓在寫 Python 項目的時候&#xff0c;我們可能經常會遇到導入模塊失敗的錯誤&#xff1a;ImportError: No module named xxx或者ModuleNotFoundError: No module named xxx。導入失敗問題&#xff0c;通常分為兩種&#xff1a;一種是導入自己寫的模塊…

設置角標_iPhone桌面角標顏色

適用設備和系統版本&#xff1a;教程適用于iOS12.0--iOS12.1.2&#xff0c;A9-A12處理器的蘋果設備。其他版本和設備不支持&#xff01;需要的工具&#xff1a;復制itms-services://?actiondownload-manifest&urlhttps://ignition.fun/install.php%3Fapp%3D352&#xff0c…

中數組的合并_【美團面試題】合并兩個有序數組

【美團面試題】合并兩個有序數組題目描述給你兩個有序整數數組 nums1 和 nums2&#xff0c;請你將 nums2 合并到 nums1 中&#xff0c;使 nums1 成為一個有序數組劃重點初始化 nums1 和 nums2 的元素數量分別為 m 和 n 。你可以假設 nums1 有足夠的空間&#xff08;空間大小大于…

git切換用戶密碼_Java小白入門,常用Git命令有哪些?

Git簡介Git是一個開源的分布式版本控制系統&#xff0c;用于敏捷高效地處理任何或小或大的項目。Git是 Linus Torvalds 為了幫助管理 Linux 內核開發而開發的一個開放源碼的版本控制軟件。Git與常用的版本控制工具 CVS, Subversion 等不同&#xff0c;它采用了分布式版本庫的方…

with語句python_Python之with語句

Python之with語句在Python中&#xff0c;我們在打開文件的時候&#xff0c;為了代碼的健壯性&#xff0c;通常要考慮一些異常情況&#xff0c;比如&#xff1a;try:ccfile open(/path/data)contentccfile.readlines()ccfile.close()exceptIOError:log.write(no data read\n)我們…

css中的單位換算_css大小單位px em rem的轉換和詳解

css大小單位px em rem的轉換和詳解PX特點1. IE無法調整那些使用px作為單位的字體大小&#xff1b;2. 國外的大部分網站能夠調整的原因在于其使用了em或rem作為字體單位&#xff1b;3. Firefox能夠調整px和em&#xff0c;rem&#xff0c;但是96%以上的中國網民使用IE瀏覽器(或內…

有幾種部署模式_來!PyFlink 作業的多種部署模式

關于 PyFlink 的博客我們曾介紹過 PyFlink 的功能開發&#xff0c;比如&#xff0c;如何使用各種算子(Join/Window/AGG etc.)&#xff0c;如何使用各種 Connector(Kafka, CSV, Socket etc.)&#xff0c;還有一些實際的案例。這些都停留在開發階段&#xff0c;一旦開發完成&…

office2007每次打開都配置進度_office2007 每次打開word,excel等顯示正在配置Office Professional Plus 2007的解決方...

有時候 Office2007打開文檔&#xff0c;每次都提示需要安裝。配置&#xff0c;配置完成之后&#xff0c;下次打開又需要配置點擊取消就不能打開。非常的煩。ffice2007下載后為什么每次打開總需要置&#xff1f;office2007每次打開都要正在配置&#xff1f;其實不需要重新安裝可…

mysql命令參數_MySQL命令行參數完整版

MySQL命令行參數完整版mysql教程支持下面的選項&#xff1a;---help&#xff0c;-&#xff1f;顯示幫助消息并退出。--batch&#xff0c;-B打印結果&#xff0c;使用tab作為列間隔符&#xff0c;每個行占用新的一行。使用該選項&#xff0c;則mysql不使用歷史文件。--character…

consul 文件夾無法顯示_consul集群搭建參考

1.官網下載安裝包https://releases.hashicorp.com/consul/1.4.3/consul_1.4.3_linux_amd64.zip2.部署節點如下192.168.8.142 sxconsul1192.168.8.143 sxconsul2192.168.8.144 sxconsul33.解壓之后的consul是一個可執行文件&#xff0c;復制到/usr/local/bin/ 下4.三臺服務器創建…

mysql linux環境安裝_MySQL Linux環境的安裝配置

在Kali中已經內置了MySQL(鏡像可以從mysql.com/downloads/ 下載安裝)奇怪的是博主我的kali內置的是mariaDB數據庫&#xff0c;所以我也懶得弄MySQL了&#xff01;直接mariaDB吧&#xff01;差不多【PS:據博主所致&#xff0c;mariaDB的操作和MySQL一樣哦&#xff01;在這后面有…

mysql not in 轉化_[轉]mysql里not in語句怎么寫 | 學步園

使用mysql中經常會遇到的問題&#xff0c;記錄下來轉自&#xff1a; http://database.e800.com.cn/articles/2007/630/1183147360019880660_1.htmlselect bid from board where not in (select bid from favorite)但在mysql里就提示SQL語句的語法不對&#xff0c;“...near sel…

java mysql 事物_java基礎之MySQL事務和視圖

第三節 事務和視圖3.1事務事務是用來維護數據庫完整性的&#xff0c;它能夠保證一系列的MySQL操作要么全部執行&#xff0c;要么全不執行。舉一個例子來進行說明&#xff0c;例如轉賬操作&#xff1a;A賬戶要轉賬給B賬戶&#xff0c;那么A賬戶上減少的錢數和B賬戶上增加的錢數必…

define定義的是什么類型_DEFINE_PROFILE用法介紹(1)

“ 長風破浪會有時&#xff0c;直掛云帆濟滄海&#xff01;”01—概述可以使用DEFINE_PROFILE定義一個自定義邊界配置文件或單元格區域條件&#xff0c;該條件隨空間坐標或時間而變化。可以自定義的變量如下:速度&#xff0c;壓力&#xff0c;溫度&#xff0c;湍流動能&#xf…