知識點目錄 >傳送門 首先推薦兩篇大牛寫的委托和事件的博客,寫的超級好!看了就包你看會,想學習的朋友直接看這兩篇就足以,我自己寫的是算是自己學習的紀錄。 傳送門 》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