c#url拼接方法名_C# 从1到Core委托与事件
委托與事件在C#1.0的時候就有了,隨著C#版本的不斷更新,有些寫法和功能也在不斷改變。本文溫故一下這些改變,以及在NET Core中關(guān)于事件的一點(diǎn)改變。
一、C#1.0?從委托開始
1. 基本方式
什么是委托,就不說概念了,用例子說話。
某HR說他需要招聘一個6年 .NET5 研發(fā)經(jīng)驗(yàn)的“高級”工程師,他想找人(委托)別人把這條招聘消息發(fā)出去。這樣的HR很多,所以大家定義了一個通用的發(fā)消息規(guī)則:
public delegate string SendDelegate(string message);這就像一個接口的方法,沒有實(shí)際的實(shí)現(xiàn)代碼,只是定義了這個方法有一個string的參數(shù)和返回值。所有想發(fā)招聘消息的HR只要遵守這樣的規(guī)則即可。
委托本質(zhì)上是一個類,所以它可以被定義在其他類的內(nèi)部或外部,根據(jù)實(shí)際引用關(guān)系考慮即可。本例單獨(dú)定義在外部。
為HR定義了一個名為HR的類:
public class HR{ public SendDelegate sendDelegate; public void SendMessage(string msg) { sendDelegate(msg); }}HR有一個SendDelegate類型的成員,當(dāng)它需要發(fā)送消息(SendMessage)的時候,只需要調(diào)用這個sendDelegate方法即可。而不需要實(shí)現(xiàn)這個方法,也不需要關(guān)心這個方法是怎么實(shí)現(xiàn)的。
當(dāng)知道這個HR需要發(fā)送消息的時候,獵頭張三接了這個幫忙招人的工作。獵頭的類為Sender,他有一個用于發(fā)送消息的方法Send,該方法恰好符合眾人定義的名為SendDelegate的發(fā)消息規(guī)則。這有點(diǎn)像實(shí)現(xiàn)了一個接口方法,但這里不要求方法名一致,只是要求方法的簽名一致。
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(); //獵頭張三來監(jiān)聽,聽到HR發(fā)什么消息后立刻傳播出去 Sender senderZS = new Sender("張三"); hr.sendDelegate = senderZS.Send; //HR遞交消息 hr.SendMessage("Hello World");}獵頭將自己的發(fā)消息方法“賦值”給了HR的SendDelegate方法,為什么可以“賦值”? 因?yàn)槎叨甲袷豐endDelegate規(guī)則。 就像A和B兩個變量都是int類型的時候,A可以賦值給B一樣。
這就是一個簡單的委托過程,HR將招人的工作委托給了獵頭,自己不用去做招人的工作。
但經(jīng)常一個招聘工作經(jīng)常會有多個獵頭接單,那就有了多播委托。
2. 多播委托
?看一下下面的代碼:
public void Test(){ //一個HR HR hr = new HR(); //獵頭張三來監(jiān)聽,聽到HR發(fā)什么消息后立刻傳播出去 Sender senderZS = new Sender("張三"); hr.sendDelegate = senderZS.Send; //快嘴李四也來了 Sender senderLS = new Sender("李四"); hr.sendDelegate += senderLS.Send; //HR遞交消息 hr.SendMessage("Hello World");}與之前的代碼改變不大, 只是添加了李四的方法綁定,這樣HR發(fā)消息的時候,張三和李四都會發(fā)出招人的消息。
這里要注意李四綁定方法的時候,用的是+=而不是=,就像拼接字符串一樣,是拼接而不是賦值,否則會覆蓋掉之前張三的方法綁定。
對于第一個綁定的張三,可以用=號也可以用+=(記得之前好像第一個必須用=,實(shí)驗(yàn)了一下現(xiàn)在二者皆可)。
這同時也暴露了一些問題:
如果后面的獵頭接單的時候不小心(故意)用了=號, 那么最終前面的人的綁定都沒有了,那么他將獨(dú)占這個HR客戶,HR發(fā)出的消息只有他能收到。
可以偷偷的調(diào)用獵頭的hr.sendDelegate
3. 通過方法避免風(fēng)險
很自然想到采用類似Get和Set的方式避免上面的問題。既然委托可以像變量一樣賦值,那么也可以通過參數(shù)來傳值,將一個方法作為參數(shù)傳遞。
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); } }經(jīng)過改造后的HR,SendDelegate方法被設(shè)置為了private,之后只能通過Add和Remove的方法進(jìn)行方法綁定。
4.模擬多播委托機(jī)制
通過上面委托的表現(xiàn)來看,委托就像是保存了一個相同方法名的集合?List?,可以向集合中添加或移除方法,當(dāng)調(diào)用這個委托的時候,會逐一調(diào)用該集合中的各個方法。
例如下面的代碼( 注意這里假設(shè)SendDelegate只對應(yīng)一個方法?):
public class HR1{ public void Delegate(SendDelegate sendDelegate) { sendDelegateList = new List { sendDelegate }; //對應(yīng)= } public void AddDelegate(SendDelegate sendDelegate) { sendDelegateList.Add(sendDelegate); //對應(yīng)+= } public void RomoveDelegate(SendDelegate sendDelegate) { sendDelegateList.Remove(sendDelegate);//對應(yīng)-= } public List sendDelegateList; public void SendMessage(string msg) { foreach (var item in sendDelegateList) { item(msg); } }}二、C#1.0?引入事件
1.簡單事件
如果既想使用-=和+=的方便,又想避免相關(guān)功能開閉的風(fēng)險怎么辦呢?可以使用事件:
public class HRWithEvent { public event SendDelegate sendDelegate; public void SendMessage(string msg) { sendDelegate(msg); } }只是將SendDelegate前面添加了一個event標(biāo)識,雖然它被設(shè)置為public,但如下代碼卻會給出錯誤提示:?事件“HRWithEvent.sendDelegate”只能出現(xiàn)在 += 或 -= 的左邊(從類型“HRWithEvent”中使用時除外)?
hr.sendDelegate = senderZS.Send; hr.sendDelegate("偷偷的發(fā)一條");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方法一樣,對事件的綁定與移除進(jìn)行條件約束。?
3. 控制綁定事件的執(zhí)行
當(dāng)多個委托被綁定到事件之后,如果想精確控制各個委托的運(yùn)行怎么辦,比如返回值(雖然經(jīng)常為void)、異常處理等。
第一章第4節(jié)通過一個List 模擬了多播委托的綁定。 會想到如果真能循環(huán)調(diào)用一個個已綁定的委托,就可以精確的進(jìn)行控制了。那么這里說一下這樣的方法:
public class HRWithEvent { public event SendDelegate sendDelegate; public void SendMessage(string msg) { //sendDelegate(msg); 此處不再一次性調(diào)用所有 if (sendDelegate != null) { Delegate[] delegates = sendDelegate.GetInvocationList(); //獲取所有已綁定的委托 foreach (var item in delegates) { ((SendDelegate)item).Invoke(msg); //逐一調(diào)用 } } } }這里通過Invoke方法逐一調(diào)用各個Delegate,從而實(shí)現(xiàn)對每一個Delegate的調(diào)用的控制。若需要異步調(diào)用,則可以通過BeginInvoke方法實(shí)現(xiàn)(.NET Core之后不再支持此方法,后面會介紹。)
((SendDelegate)item).BeginInvoke(msg,null,null);4. 標(biāo)準(zhǔn)的事件寫法
.NET 事件委托的標(biāo)準(zhǔn)簽名是:
void OnEventRaised(object sender, EventArgs args);返回類型為 void。?事件基于委托,而且是多播委托。?參數(shù)列表包含兩種參數(shù):發(fā)件人和事件參數(shù)。?sender?的編譯時類型為?System.Object。
第二種參數(shù)通常是派生自?System.EventArgs?的類型(.NET Core 中已不強(qiáng)制要求繼承自System.EventArgs,后面會說到)。
將上面的例子修改一下,改成標(biāo)準(zhǔn)寫法,大概是下面代碼的樣子:
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 為最后一個的值 因?yàn)楦采w }}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?的時候,隨著泛型出現(xiàn),支持了泛型委托,例如,在委托的簽名中可以使用泛型,例如下面代碼
public delegate string SendDelegate(T message);這樣的委托適用于不同的參數(shù)類型,例如如下代碼(注意使用的時候要對應(yīng)具體的類型)
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; // 注意使用的時候要對應(yīng)具體的類型 hr1.sendDelegate2 = new SendDelegate<int>(Sender1.Send2); hr1.sendDelegate3 = delegate (DateTime dateTime) { return dateTime.ToLongDateString(); }; }}2. C#2.0 delegate運(yùn)算符
delegate?運(yùn)算符創(chuàng)建一個可以轉(zhuǎn)換為委托類型的匿名方法:
例如上例中這樣的代碼:
hr1.sendDelegate3 = delegate (DateTime dateTime) { return dateTime.ToLongDateString(); };3. C#3.0?Lambda 表達(dá)式
從 C# 3 開始,lambda 表達(dá)式提供了一種更簡潔和富有表現(xiàn)力的方式來創(chuàng)建匿名函數(shù)。?使用?=> 運(yùn)算符構(gòu)造 lambda 表達(dá)式,
例如“delegate運(yùn)算符”的例子可以簡化為如下代碼:
hr1.sendDelegate3 = (dateTime) => { return dateTime.ToLongDateString(); };4.C#3,NET Framework3.5,Action 、Func、Predicate
Action 、Func、Predicate本質(zhì)上是框架為我們預(yù)定義的委托,在上面的例子中,我們使用委托的時候,首先要定義一個委托類型,然后在實(shí)際使用的地方使用,而使用委托只要求方法名相同,在泛型委托出現(xiàn)之后,“定義委托”這一操作就顯得越來越累贅,為此,系統(tǒng)為我們預(yù)定義了一系列的委托,我們只要使用即可。
例如Action的代碼如下:
實(shí)際上定義了最多16個參數(shù)的無返回值的委托。
Func與此類似,是最多16個參數(shù)的有返回值的委托。Predicate則是固定一個參數(shù)以及bool類型返回值的委托。
public delegate bool Predicate(T obj);?5. .NET Core?異步調(diào)用
第2.3節(jié)中,提示如下代碼在.NET Core中已不支持
((SendDelegate)item).BeginInvoke(msg,null,null);會拋出異常:
System.PlatformNotSupportedException:“Operation is not supported on this platform.”需要異步調(diào)用的時候可以采用如下寫法:
Task task = Task.Run(() => ((SendDelegate)item).Invoke(msg));對應(yīng)的?EndInvoke()?則改為:?task.Wait();?
?5. .NET Core的 EventHandler
.NET Core 版本中,EventHandler?定義不再要求?TEventArgs?必須是派生自?System.EventArgs?的類, 使我們使用起來更為靈活。
例如我們可以有這樣的寫法:
EventHandler<string> SendNew?這在以前的版本中是不允許的。
總結(jié)
以上是生活随笔為你收集整理的c#url拼接方法名_C# 从1到Core委托与事件的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: mysql最大值最小值_mysql最大值
- 下一篇: c#和python更适合爬虫_pytho