c#中的代理和事件

事件(event)是一個非常重要的概念,我們的程序時刻都在觸發和接收著各種事件:鼠標點擊事件,鍵盤事件,以及處理操作系統的各種事件。所謂事件就是由某個對象發出的消息。比如用戶按下了某個按鈕,某個文件發生了改變,socket上有數據到達。觸發事件的對象稱作發送者(sender),捕獲事件并且做出響應的對象稱作接收者(receiver),一個事件可以存在多個接受者。

在異步機制中,事件是線程之間進行通信的一個非常常用的方式。比如:用戶在界面上按下一個按鈕,執行某項耗時的任務。程序此時啟動一個線程來處理這個任務,用戶界面上顯示一個進度條指示用戶任務執行的狀態。這個功能就可以使用事件來進行處理。可以將處理任務的類作為消息的發送者,任務開始時,發出“TaskStart”事件,任務進行中的不同時刻發出“TaskDoing”事件,并且攜帶參數說明任務進行的比例,任務結束的時候發出“TaskDone”事件,在畫面中接收并且處理這些事件。這樣實現了功能,并且界面和后臺執行任務的模塊耦合程度也是最低的。

具體說C#語言,事件的實現依賴于“代理”(delegate)的概念,先了解一下代理。

代理(delegate

delegate是C#中的一種類型,它實際上是一個能夠持有對某個方法的引用的類。與其它的類不同,delegate類能夠擁有一個簽名(signature),并且它只能持有與它的簽名相匹配的方法的引用。它所實現的功能與C/C++中的函數指針十分相似。它允許你傳遞一個類A的方法m給另一個類B的對象,使得類B的對象能夠調用這個方法m。但與函數指針相比,delegate有許多函數指針不具備的優點。首先,函數指針只能指向靜態函數,而delegate既可以引用靜態函數,又可以引用非靜態成員函數。在引用非靜態成員函數時,delegate不但保存了對此函數入口指針的引用,而且還保存了調用此函數的類實例的引用。其次,與函數指針相比,delegate是面向對象、類型安全、可靠的受控(managed)對象。也就是說,runtime能夠保證delegate指向一個有效的方法,你無須擔心delegate會指向無效地址或者越界地址。

實現一個delegate是很簡單的,通過以下3個步驟即可實現一個delegate:

1.?聲明一個delegate對象,它應當與你想要傳遞的方法具有相同的參數和返回值類型。

2.?創建delegate對象,并將你想要傳遞的函數作為參數傳入。

3.?在要實現異步調用的地方,通過上一步創建的對象來調用方法。

下面是一個簡單的例子:

public class MyDelegateTest

{

??? // 步驟1,聲明delegate對象

??? public delegate void MyDelegate(string name);

??? // 這是我們欲傳遞的方法,它與MyDelegate具有相同的參數和返回值類型

??? public static void MyDelegateFunc(string name)

??? {

??????? Console.WriteLine("Hello, {0}", name);

??? }

??? public static void Main ()

??? {

??????? // 步驟2,創建delegate對象

????? MyDelegate md = new MyDelegate(MyDelegateTest.MyDelegateFunc);

??????? // 步驟3,調用delegate

??????? md("sam1111");

??? }

}

輸出結果是:Hello, sam1111

下面我們來看看事件是如何處理的:

事件(event)

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:在適當的地方調用事件觸發方法觸發事件。

下面是一個例子,例子模仿容器和控件的模式,由控件觸發一個事件,在容器中捕捉并且進行處理。

事件的觸發者:

/// <summary>

/// 事件的觸發者

/// </summary>

public class Control

{

??? public delegate void SomeHandler(object sender, System.EventArgs e);

??? /**

???? * 可以采用系統提供的System.EventHandler, 這里為了說明情況使用了自己定義的delegate

???? * 如果需要在事件的參數中使用自己定義的類型,也要自己定義delegate

???? */

??? //public event System.EventHandler SomeEvent;

??? public event SomeHandler SomeEvent;

??? public Control()

??? {

??????? //這里使用的delegate必須與事件中聲名的一致

??????? //this.SomeEvent += new System.EventHandler(this.Control_SomeEvent);

??????? this.SomeEvent += new SomeHandler(this.ProcessSomeEvent);

??? }

??? public void RaiseSomeEvent()

??? {

??????? EventArgs e = new EventArgs();

??????? Console.Write("Please input 'a':");

??????? string s = Console.ReadLine();

??????? //在用戶輸入一個小a的情況下觸發事件,否則不觸發

??????? if (s == "a")

??????? {

??????????? SomeEvent(this, e);

??????? }

??? }

??? //事件的觸發者自己對事件進行處理,這個方法的參數必須和代理中聲名的一致

??? private void ProcessSomeEvent(object sender, EventArgs e)

??? {

??????? Console.WriteLine("hello");

??? }

}

事件的接收者:

/// <summary>

/// 事件的接收和處理者

/// </summary>

class Container

{

??? private Control ctrl = new Control();

 

??? public Container()

??? {

??????? //這里使用的delegate必須與事件中聲名的一致

??????? //ctrl.SomeEvent += new EventHandler(this.OnSomeEvent);

??????? ctrl.SomeEvent += new Control.SomeHandler(this.ResponseSomeEvent);

??????? ctrl.RaiseSomeEvent();

??? }

??? public static void Main()

??? {

??????? Container pane = new Container();

??????? //這個readline是暫停程序用的,否則畫面會一閃而過什么也看不見

??????? Console.ReadLine();

??? }

??? //這是事件的接受者對事件的響應

??? private void ResponseSomeEvent(object sender, EventArgs e)

??? {

??????? Console.WriteLine("Some event occur!");

??? }

}

程序運行的結果如下:

please input 'a':a

hello

Some event occur!

事件的應用

例如有下面的需求需要實現:程序主畫面中彈出一個子窗口。此時主畫面仍然可以接收用戶的操作(子窗口是非模態的)。子窗口上進行某些操作,根據操作的結果要在主畫面上顯示不同的數據。我發現一些程序員這樣實現這個功能:

主畫面彈出子窗口后,將自己的指針交給子畫面,然后在子畫面中使用這個指針,調用主畫面提供的方法,改變主畫面上的數據顯示。這樣雖然可以達到目的,但是各個模塊之間產生了很強的耦合。一般說來模塊之間的調用應該是單方向的:模塊A調用了模塊B,模塊B就不應該反向調用A,否則就破壞了程序的層次,加強了耦合程度,也使得功能的改變和追加變得很困難。

這時正確的做法應該是在子窗口的操作過程中發出各種事件,而由主窗口捕捉這些事件進行處理,各個模塊專心的做自己的事情,不需要過問其他模塊的事情。

轉載于:https://www.cnblogs.com/salonliudong/archive/2006/12/22/600834.html

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

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

相關文章

個人技術博客

一. Volley框架 在進行和服務器交互的時候需要發送請求&#xff0c;發現了volley這個好用易上手的框架。volley是一個異步網絡通信框架&#xff0c;它的優點在于輕量級、適用于量小但傳送頻繁的請求操作 搭建請求的第一步就是新建一個請求隊列RequestQueue queue Volley.newRe…

軟件構造 第一章第二節 軟件開發的質量屬性

?軟件構造 第一章第二節 軟件開發的質量屬性 1.軟件系統質量指標 External quality factors affect users 外部質量因素影響用戶 Internal quality factors affect the software itself and its developers 內部質量因素影響軟件本身和它的開發者 External quality results fr…

css --- 讓不同的瀏覽器加載不同的CSS

// 通過條件注釋讓不同的瀏覽器加載不同的CSS <!--[if !IE]><!--> 除IE外都可識別 <!--<![endif]--> <!--[if IE]><!--> 所有的IE可識別 <![endif]--> <!--[if IE 6]> 僅IE6可識別 <![endif]--> <!--[if lt IE 6]> I…

??? ?? ??.??

abcdefg a?? abca abcbca abcabcdeda Cc ?? ??? [a] [ac] [a-c] [Cc] ??? 1>* ( 0~???) 2> (1~???) 3.? () 4 {1,2} {Min,Max} [??]*{} ???.??…

css自媒體查詢

準備工作1&#xff1a;設置Meta標簽 首先我們在使用Media的時候需要先設置下面這段代碼&#xff0c;來兼容移動設備的展示效果&#xff1a; <meta name"viewport" content"widthdevice-width, initial-scale1.0, maximum-scale1.0, user-scalableno">…

css --- 清除浮動

有時我們需要用到浮動,但又不想由于浮動的某些特性影響布局,這時就需要清除浮動 清除浮動主要應用的是CSS中的clear屬性,clear屬性定義了元素的哪一側不允許出現浮動元素. 下面是兩種應用比較廣泛的清除浮動的方法: // 在需要的地方添加定義了clear:both的空標簽 <style>…

數據可視化實現技術(canvas/svg/webGL)

數據可視化的實現技術和工具比較轉載于:https://www.cnblogs.com/knuzy/p/9215632.html

Python 字符串操作(string替換、刪除、截取、復制、連接、比較、查找、包含、大小寫轉換、分割等)...

http://www.cnblogs.com/huangcong/archis.strip() .lstrip() .rstrip(,) 去空格及特殊符號復制字符串Python1 #strcpy(sStr1,sStr2)2 sStr1 strcpy3 sStr2 sStr14 sStr1 strcpy25 print sStr2連接字符串Python1 #strcat(sStr1,sStr2)2 sStr1 strcat3 sStr2 append4 sStr1…

java 將一個非空文件夾拷貝到另一個地方

沒有處理異常&#xff0c;只是簡單的拋出了。需要捕獲的需修改一下。 public class Test001 { //把一個文件夾或文件移到另一個地方去。 public static void main(String[] args) throws Exception { File filenew File("D:\\testFolder"); new Test001().copyFileTo…

Python環境 及安裝

windows 1、下載安裝包 https://www.python.org/downloads/2、安裝默認安裝路徑&#xff1a;C:\python273、配置環境變量【右鍵計算機】--》【屬性】--》【高級系統設置】--》【高級】--》【環境變量】--》【在第二個內容框中找到 變量名為Path 的一行&#xff0c;雙擊】 -->…

MUI主界面菜單同時移動主體部分不出滾動條解決

mOffcanvas(側滑導航-主界面、菜單同時移動) 生成代碼 增加列表滾動OK 增加幻燈片就掛了 百度了半天 沒發現問題 后來想起官網的一句話 除頂部導航、底部選項卡兩個控件之外&#xff0c;其它控件都建議放在.mui-content控件內&#xff0c;在Hbuilder中輸入mbody&#xff0c;可快…

范圍查詢 BETWEEN AND

查詢&#xff1a;從表t_student里 查找 id 在1~10 之間的學生信息&#xff0c;并顯示 id,name,age,email 信息 SELECT id,name,age,email FROM t_student WHERE id BETWEEN 1 AND 10轉載于:https://www.cnblogs.com/hello-dummy/p/9216720.html

css --- 應用媒介查詢制作響應式導航欄

以上導航會自動適應各個尺寸的屏幕 代碼如下: <!DOCTYPE html> <html> <head> <meta charset"utf-8"> <meta name"viewport" content"widthdevice-width, initial-scale1.0"> <meta name"apple-mobile-w…

AODV中實施watchdog

1.開啟混雜模式&#xff0c;接收鄰居轉發的所有數據包&#xff0c;不進行MAC過濾&#xff0c;全部 sendToWatchdog(const Packet *p) 2.當前節點轉發數據包后&#xff0c;緩存數據包 sendToWatchdog(const Packet *p, int mac_dst) //Start Watchdog Code voidAODV::tap(const …

Drools 7.4.1.Final參考手冊(六) 用戶手冊

用戶手冊 基礎 無狀態的知識Session Drools規則引擎擁有大量的用例和功能&#xff0c;我們要如何開始&#xff1f;你無須擔心&#xff0c;這些復雜性是分層的&#xff0c;你可以用簡單的用例來逐步入門。 無狀態Session&#xff0c;無須使用推理&#xff0c;就形成了最簡單的用…

css --- 彈性盒子

左右兩側按1:1自適應,中間固定寬度500px // CSS /* 首先定義 container &#xff0c;關注display */ /* webkit 是Chrome、Safari 的瀏覽器前綴 */ .container{margin: auto;display: -webkit-box;width: 80%&#xff1b;height: 200px; } /* 開始定義左、中、右 */ .left { …

更精煉更專注的RTMPClient客戶端EasyRTMPClient,滿足直播、轉發、分析等各種需求...

現狀 EasyRTMPClient&#xff0c;熟悉的朋友就會聯想到EasyRTSPClient項目&#xff08;https://github.com/EasyDSS/EasyRTSPClient&#xff09;&#xff0c;EasyRTSPClient從14年中期開始發展&#xff0c;已經迭代發展歷時3年多時間&#xff0c;可以說在RTSPClient領域是非常成…

數據操縱語言 ,DML, 增刪改

DML(data manipulation language)&#xff1b;數據操縱語言 插入數據&#xff1a;插入數據&#xff1a; 每次只能插入一行數據 INSERT INTO 表名(列名1, 列名2, 列名3....) VALUES (value1, value2, value3....); value與列數一一對應。 #插入一行: INSERT INTO t_student(id,…

bootstrap --- 表格

幾個可選類: .table-bordered: 為表格加上邊框 .table-striped: 為表格加上斑馬效果 .table-hover: 鼠標懸停在表格行上時展現不同得顏色 .table-condensed: 更為緊湊的表格樣式 // 導入樣式,從bootstrap官網導入CDN // HEAD <link rel"stylesheet" href"…

發現一個小坑的地方,unity的協程,想要停止,必須以字符串啟動

今天想要停止一個協成&#xff0c;發現調用 StopCoroutine(ShowDebug()); 竟然不管用&#xff0c;后來看了文檔才知道&#xff0c;原來想要停止協成&#xff0c;必須用字符啟動協程 StartCoroutine("ShowDebug"); 然后再調用 StopCoroutine("ShowDebug…