C# 中的委托和事件(詳解)
https://www.cnblogs.com/newcapecjmc/p/7084026.html
基礎:https://www.cnblogs.com/hongfei/p/3574239.html
中級:http://www.tracefact.net/tech/009.html
https://blog.csdn.net/yijun494610095/article/details/62422746
進階:https://www.cnblogs.com/sjqq/p/6917497.html
關於委托和事件分享三個博客內容:
首先說明下:
(1)C#中事件:事件時屬於類的成員,所以要放在類的內部。
(2)委托屬於一個定義,是和類、接口類似的,通常放在外部。
(因為大多數委托都要被重用)
委托定義在類里面還是類外面視情況而定,一般定義在與類定義平級部分,
且用public修飾,便於外部調用。
若定義在類的內部,則必須通過調用該類的成員才能取得其委托的引用,
頻繁調用的情況下不合適。
*委托定義在類內部,舉例說明如下:
Class A { //聲明在這里是可以的~ private delegate void test_del(int a); private void test(){...} public void start(){ //聲明在這里就不行了~ private delegate void test_del(int a); } }
委托是一種特殊的類,和普通類不同的是委托是對一類方法的抽象。
因此只能在類內部定義內部類(包括委托、結構和枚舉)而不能在方法中定義
第一個:
再談C#委托與事件
轉自:http://ruizhinet.blog.163.com/blog/static/9921382820092801032681/
之前寫過一篇關於C#委托與事件的文章(見《C#委托和事件例析》),不過還是收到一些網友的提問。所以,今天再換另一個角度來詳解一下這個問題。
一、在控制台下使用委托和事件
我們都知道,C#中有“接口”這個概念,所謂的“接口”就是定義一套標准,然后由實現類來具體實現其中的方法,所以說“接口,是一組類的抽象”。同樣道理,我們可以將“委托”理解為“方法的抽象”,也就是說定義一個方法的模板,至於這個方法具體是怎么樣的,就由方法自己去實現。
我們知道接口的最大好處就是可以實現多態,同理,“委托”是可以實現方法的多態,當我們想調用某個具體方法的時候,我們不直接調用這個方法,而是去調用這個委托。當然,我們必須在具體方法和委托之間建立某種關聯。
下面我們來看例子。
首先,我們定義一個委托:
public delegate void SaySomething(string name);
這跟抽象方法的語法格式很相似,只是多了一個關鍵字delegate。既然是對方法的一種抽象,那么我們最關注的當然就是方法的返回值以及方法的參數了。所以上面紅色的部分就是我們定義出來的一個規矩,如果某個方法想委托我去做事,那么請你遵循我的規矩,就是返回值為void,參數為一個字符串。我們這個委托的含義是,當某個人來了,就向他說點東西。
好,既然我們已經定義了這個規矩,下面我們就定義具體的方法了。
public void SayHello(string name) { Console.WriteLine("Hello," + name + "!"); }
public void SayNiceToMeetYou(string name) { Console.WriteLine("Nice to meet you," + name + "!"); }
我們這里一共定義了兩個方法,一個是向某人說Hello,另一個是向某人說Nice to meet you。我們看到,這里定義的兩個方法的返回值和參數跟我們前面定義的“委托”是一致的。
接下來,我們來看事件。
public event SaySomething come;
我們定義了一個事件,這個事件是“有人來了”,注意定義的時候我們使用event關鍵字,除此之外,我們還加上了前面定義的“委托”的名字。這個意思是說,我這個事件只會跟“SaySomething”打交道,並且,當我這個事件發生的時候,我會通知關注我的這些“委托”(再由這些“委托”去調用具體的方法)。
我們來定義一個測試方法:
public void test() { SaySomething sayhello = new SaySomething(SayHello); SaySomething saynice = new SaySomething(SayNiceToMeetYou); come += sayhello; come += saynice; come("張三"); }
方法體中的前面兩行是用來實例化委托,注意我們用到了new關鍵字,就好像實例化一個類一樣,然后傳入一個參數,但這個參數不是string類型、也不是int類型,而是一個方法名。
再下面兩行就是將委托加到事件上,意思是說,如果你這個事件發生了,就告訴我一聲。可以通過“+=”來將n個委托實例加到某個事件上,一旦這個事件發生,所有的這些委托實例都會得到通知。
最后一行是觸發一個事件,注意我們是直接用一個事件名,然后跟一個參數,這又跟“委托”中定義的那個規矩一致(即,要有一個string類型的參數)。
最后運行一下
static void Main(string[] args) { Program program = new Program(); program.test(); Console.Read(); }
我們回過頭來再看一下“事件”的定義:
public event SaySomething come;
這里已經指出了“委托”的名字,所以,我們可以直接將方法加到事件上,而省略“委托”的實例化過程,因此上面的test()方法可以簡單寫為:
public void test() { come += SayHello; come += SayNiceToMeetYou; come("張三"); }
二、在窗體中使用委托和事件
上面的例子並不能體現委托和事件的優點,其實,委托和事件在C#中使用非常廣泛,例如,當我們點擊某個“按鈕”的時候,就會有一個“Click”事件觸發,而這個事件會通知“委托”,在C#窗體應用程序中,“委托”的名字比較規范,統一使用“EventHandler”,它的具體格式是“void EventHandler(object sender, EventArgs e);”。相信大家都寫過下面這樣子的HelloWorld程序:
當點擊按鈕的時候彈出一個對話框。我們怎樣實現的呢?你肯定會說,我們在設計窗口雙擊按鈕,就會自動為我們生成類似如下的方法:
private void button1_Click(object sender, EventArgs e) { MessageBox.Show("我被點擊了!!!"); }
其實,這里用到的就是事件和委托,這里的button1_Click就是符合EventHandler委托規矩的一個具體的方法,即返回值為void,參數分別是一個object和EventArgs。
我們可以在Form1.Designer.cs中看到如下代碼:
this.button1.Click += new System.EventHandler(this.button1_Click);
可以看到,這里有一個Click事件,然后將一個委托實例附加到這個事件上,跟我們前面講的控制台應用程序中的用法是完全一樣的。那這個Click事件是怎么觸發的呢?對於這些系統類的事件,並不用我們管。
當然,我們也可以定義自己的事件和委托,例如我定義一個事件,這個事件就是輸出對象的名字。
我們這里定義了一個ShowName委托和一個btnclick事件。並且,在button1_Click()方法中觸發這個btnclick事件。最后的結果是,當我們點擊按鈕的時候,首先彈出一個“我被點擊了!!!”的對話框,然后確定之后再彈出另一個顯示按鈕名稱的對話框:
第二個
C#委托和事件例析
ah_bill是對Java了解相對較多,而對C#則是因工作需要才去看了一下,C#跟Java在語法上非常相似,而最初讓我比較困惑的就是委托、事件部分,相信大多數初學者也有類似的困惑。經過跟Java的對比學習,發現這其實跟Java的監聽、事件是等同的,只是表述上不同罷了。
委托+事件是觀察者模式的一個典型例子,所謂的委托其實就是觀察者,它會關心某種事件,一旦這種事件被觸發,這個觀察者就會行動。
下面是最近寫的一個例子,相信能夠加深大家對委托和事件的理解。
using System; using System.Collections.Generic; using System.Text;
namespace ConsoleApplication3
{
public delegate void TimeEventHandler(object obj, TimeEventArgs args);
//定義一個委托,委托其實就是“方法模板”,就好像“類”是“對象”的模板一樣。如果某個類想在事件觸發的時候收到通知,它必須有一個符合這種格式的方法,在這個例子中,就是:返回類型為void,參數類型為object、TimeEventArgs。
//TimeEventArgs是我們自己定義的一個類,用於保存事件中的參數。這里我們分別保存時間的時分秒。 public class TimeEventArgs:EventArgs { private int hour; private int minute; private int second; public TimeEventArgs(int hour, int minute, int second) { this.hour = hour; this.minute = minute; this.second = second; } public int Hour { get { return hour; } } public int Minute { get { return minute; } } public int Second { get { return second; } } } //這是一個觀察者類,它有一個符合我們上面定義的“委托”的方法,也就是void ShowTime(object obj, TimeEventArgs args),從這個方法的定義可以看到,我們只會關心返回類型和方法的參數,而方法名稱則無所謂。 class MyTimeEventHandlerClass { public void ShowTime(object obj, TimeEventArgs args) { Console.WriteLine("現在的時間是:"+args.Hour+":"+args.Minute+":"+args.Second); } } //時鍾類 class Clock { //我們在這個類中定義了一個“TimeChanged”事件,注意其前面有兩個關鍵字“event”和“TimeEventHandler”,其中event表示這是一個事件,而不是方法或屬性;TimeEventHandler則指出,誰要監聽TimeChanged事件,它就必須有一個符合TimeEventHandler(委托)的方法。 public event TimeEventHandler TimeChanged; public Clock() { TimeChanged = null; //注意,這里的null的含義是指TimeChanged事件當前還沒有觀察者關注它,如果某個觀察者要關注TimeChanged事件,它必須要讓這個事件知道,方法是使用操作符“+=”來借助委托將其加載到事件上。 } //時鍾開始走動,我們的目標是每秒鍾觸發一次TimeChanged事件 public void go() { DateTime initi = DateTime.Now; int h1 = initi.Hour; int m1 = initi.Minute; int s1 = initi.Second; while (true) { DateTime now = DateTime.Now; int h2 = now.Hour; int m2 = now.Minute; int s2 = now.Second; if (s2!=s1) { h1 = h2; m1 = m2; s1 = s2; //首先建立一個TimeEventArgs對象來保存相關參數,這里是時分秒。 TimeEventArgs args = new TimeEventArgs(h2,m2, s2); //注意這種寫法,這一句是用來觸發事件,事件不是類,所以不用使用“new”關鍵字,而且我們看到,這里TimeChanged的兩個參數跟我們的委托(TimeEventHandler)是一致的,其中第一個參數是觸發這個事件的對象,我們這里使用的是一個時鍾實例(this)。 TimeChanged(this, args); } } } } class Program { static void Main(string[] args) { Clock clock = new Clock(); //實例化一個時鍾 MyTimeEventHandlerClass tehc = new MyTimeEventHandlerClass(); //實例化一個觀察者類 //將事件跟我們定義的觀察者進行連接,這樣,clock就會知道,每當TimeChanged事件被觸發,就會去通知這個觀察者,注意我們連接的時候使用的並不是直接的觀察者類實例中的ShowTime()方法,而是一個委托,並在這個委托中傳遞ShowTime()方法,這也是“委托”的真正意義所在——我有一個方法,但我委托你來幫我關聯到事件,因為事件只會直接跟委托打交道,而不是觀察者的具體某個方法。 clock.TimeChanged+=new TimeEventHandler(tehc.ShowTime); clock.go(); } } }
第三個博文:
C#中的委托到底是什么概念??
委托,簡單理解是這樣的.
比如您要管您的孩子,把孩子送進了幼兒園.
OK.此時您就把您的孩子委托給了幼兒園.
當幼兒園放學,將孩子交還給您的手中.則是委托的回調.
當然我這里的例子是說異步委托調用.您也可以同步.
什么是同步委托調用?
您去銀行存錢.把錢給櫃員.他們幫你點鈔.存款然后給您存折或卡.
那么您就相當於把錢委托給了櫃員讓他去執行存錢的這個函數.
明白了么?
Delegate
delegate是C#中的一種類型,它實際上是一個能夠持有對某個方法的引用的類。與其它的類不同,delegate類能夠擁有一個簽名(signature),並且它"只能持有與它的簽名相匹配的方法的引用"。它所實現的功能與C/C++中的函數指針十分相似。它允許你傳遞一個類A的方法m給另一個類B的對象,使得類B的對象能夠調用這個方法m。但與函數指針相比,delegate有許多函數委托和事件在 .Net Framework中的應用非常廣泛指針不具備的優點。首先,函數指針只能指向靜態函數,而delegate既可以引用靜態函數,又可以引用非靜態成員函數。在引用非靜態成員函數時,delegate不但保存了對此函數入口指針的引用,而且還保存了調用此函數的類實例的引用。其次,與函數指針相比,delegate是面向對象、類型安全、可靠的受控(managed)對象。也就是說,runtime能夠保證delegate指向一個有效的方法,你無須擔心delegate會指向無效地址或者越界地址。
實現一個delegate是很簡單的,通過以下3個步驟即可實現一個delegate:
1. 聲明一個delegate對象,它應當與你想要傳遞的方法具有相同的參數和返回值類型。
2. 創建delegate對象,並"將你想要傳遞的函數作為參數傳入"。
3. 在要實現異步調用的地方,通過上一步創建的對象來調用方法。
using System; public class MyDelegateTest { // 步驟1,聲明delegate對象 public delegate void MyDelegate(string name); // 這是我們欲傳遞的方法,它與MyDelegate具有相同的參數和返回值類型 public static void MyDelegateFunc(string name) { Console.WriteLine("Hello, ", name); } public static void Main() { // 步驟2,創建delegate對象(實例??) MyDelegate md = new MyDelegate(MyDelegateTest.MyDelegateFunc); // 步驟3,調用delegate md("sam1111"); } }
輸出結果是:Hello, sam1111
了解了delegate,下面我們來看看,在C#中對事件是如何處理的。
C#中的事件處理實際上是一種具有特殊簽名的delegate,象下面這個樣子:
public delegate void MyEventHandler(object sender, MyEventArgs e);
其中的兩個參數,sender代表事件發送者,e是事件參數類。MyEventArgs類用來包含與事件相關的數據,所有的事件參數類都必須從System.EventArgs類派生。當然,如果你的事件不含參數,那么可以直接用System.EventArgs類作為參數。
就是這么簡單,結合delegate的實現,我們可以將自定義事件的實現歸結為以下幾步:
1.定義delegate對象類型,它有兩個參數,第一個參數是事件發送者對象,第二個參數是事件參數類對象。
2.定義事件參數類,此類應當從System.EventArgs類派生。如果事件不帶參數,這一步可以省略。
3.定義"事件處理方法,它應當與delegate對象具有相同的參數和返回值類型"。
4.用event關鍵字定義事件對象,它同時也是一個delegate對象。
5.用+=操作符添加事件到事件隊列中(-=操作符能夠將事件從隊列中刪除)。
6.在需要觸發事件的地方用調用delegate的方式寫事件觸發方法。一般來說,此方法應為protected訪問限制,既不能以public方式調用,但可以被子類繼承。名字是OnEventName。
7. 在適當的地方調用事件觸發方法觸發事件。
下面是一個簡單的例子:
using System; public class EventTest { // 步驟1,定義delegate對象 public delegate void MyEventHandler(object sender, System.EventArgs e); // 步驟2(定義事件參數類)省略 public class MyEventCls { // 步驟3,定義事件處理方法,它與delegate對象具有相同的參數和返回值類型 public void MyEventFunc(object sender, System.EventArgs e) { Console.WriteLine("My event is ok!"); } } // 步驟4,用event關鍵字定義事件對象 private event MyEventHandler myevent; private MyEventCls myecls; public EventTest() { myecls = new MyEventCls(); // 步驟5,用+=操作符將事件添加到隊列中 this.myevent += new MyEventHandler(myecls.MyEventFunc); } // 步驟6,以調用delegate的方式寫事件觸發函數 protected void OnMyEvent(System.EventArgs e) { if(myevent != null) myevent(this, e); } public void RaiseEvent() { EventArgs e = new EventArgs(); // 步驟7,觸發事件 OnMyEvent(e); } public static void Main() { EventTest et = new EventTest(); Console.Write("Please input ''a'':"); string s = Console.ReadLine(); if(s == "a") { et.RaiseEvent(); } else { Console.WriteLine("Error"); } } }
輸出結果如下,紅色為用戶的輸入:
Please input ‘a’: a
My event is ok!
————————————————
版權聲明:本文為CSDN博主「LixiSchool」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/lizhenxiqnmlgb/article/details/82141968