C 深入剖析 1 事件

2022-03-26 14:19:30 字數 4452 閱讀 6221

c#深入剖析(1)——事件             

準備寫乙個系列文章,深入**c#及.net中的某些特性。

第一篇    事件

事件相信每個人都不陌生,隨便乙個winform程式,就會使用大量的事件,比如:

c# code

class

mainform : form

private

void

mainform_click(

object

sender, eventargs e)

}

當然,還可以對**進行簡化,如型別的自動推斷,匿名方法,lambda表示式等。這個事件大概的工作流程為:當使用者單擊窗體時,作業系統向應用程式傳送一系列訊息,如左鍵按下和左鍵抬起,應用程式將通過getmessage等方法最終將訊息提交到視窗過程(wndproc),視窗過程通過處理訊息,當發現產生了連續的左鍵按下和左鍵抬起的訊息後,

就知道產生了單擊事件,於是去呼叫窗體的onclick方法,該方法會去檢測一下是否訂閱了click事件,如果訂閱了,就會去呼叫相應的事件處理程式,這個過程是通過委託實現的。

下面我從語法角度來分析一下事件:

事件是類、結構或介面中的乙個成員,它有兩種定義形式:

一、 c# code

event

methodinvoker oneevent;

二、 c# code

event

methodinvoker oneevent

remove

}其中methodinvoker

是乙個沒有引數和返回值的委託,它只是用來約束事件處理程式的的形式,你可以任意定義乙個,例如你可以使用action來代替。

事件包括兩個訪問器,其中add訪問器會在訂閱事件時觸發,remove訪問器在取消事件訂閱時觸發。

對於第一種定義形式,系統會自動提供add及remove訪問器,同時會提供如下字段:

c# code

private

methodinvoker oneevnet;

該字段的型別為委託的型別,欄位名跟事件名相同(乙個類中擁有同名成員,c#編譯器是不允許的,但是系統可以)。

c# code

class

demo

}public

event

methodinvoker oneevent;

public

event

func

<

int,

string

>

twoevent;

}

private

void

button1_click(

object

sender, eventargs e)

;de.twoevent

+=arg

=>

arg.tostring();

de.invokeevent();

}

可以看出,這裡事件類似於方法和委託,可以傳遞引數並被呼叫。事實上,這只是編譯器的一種包裝,這裡其實使用的正是前面提到的同名的委託字段。而如果是在乙個類中訪問另乙個類中的事件,或者如下面將要提到的自己提供訪問器的情況,由於不存在同名的委託字段,事件就不能再這樣使用了,而只能出現在+=和-=運算子的左側。

至少有兩個理由使得我們需要自己提供訪問器:

1. 希望在訂閱或取消事件時執行一段**。

2. 前面提到,如果不提供訪問器,每定義乙個事件,系統就會生成乙個同名的委託字段,如果事件特別多,這就是一項巨大的開銷,而如果定義了訪問器,則不再提供,此時我們可以用一種統一的方式來處理,從而節省資源,實際上,winform就是這樣處理的。

c# code

class

demo

}eventhandlerlist ehl

=new

eventhandlerlist();

static

readonly

object

oneevent

=new

object

();

static

readonly

object

twoevent

=new

object

();

public

event

methodinvoker oneevent

remove

}public

event

func

<

int,

string

>

twoevent

remove

}public

void

oncall()

}class

pro

public

void

oncall()

public

static

void

oncalls()

}

private

void

button1_click(

object

sender, eventargs e)

可以看到,只有pr.oncall和pro.oncalls訂閱成功了,並且pro.oncalls不能被取消。

再來簡單說說winform事件:

winform的根是元件(component),可視的元件稱為控制項(control)。

component上定義了乙個受保護的屬性events,用於管理事件列表。

control上定義了一系列靜態的私有字段,為事件列表提供索引鍵,欄位名基本上是:

event事件名

如eventclick,evententer等。

舉乙個應用:

如何獲取乙個事件訂閱的所有方法列表,以及如何在不知道事件處理程式方法名的情況下取消事件,或者更現實一點,如何取消匿名方法(在不宣告乙個委託引用的前提下,匿名方法顯然不可能通過-=運算子來取消)。

c# code

private

void

button1_click(

object

sender, eventargs e)

}

這段**可以顯示button2的click事件訂閱的所有方法,並且在執行該段**後,button2的click事件將失效。

也許有人會有這樣的疑問:getinvocationlist獲得的委託陣列中,某個委託如果還是包括多個方法鏈怎麼辦?微軟為大家想得非常周到了——陣列中的每個委託都僅表示一種方法。

另外,如果我們需要取消所有事件,不用遍歷,直接呼叫eventhandlerlist.dispose方法即可。

關於winform事件,還有乙個有趣的現象:

當我們拖曳乙個控制項到窗體時,雙擊該控制項就會進入某個事件處理程式,例如雙擊button控制項,會進入click事件;雙擊textbox控制項,會進入textchanged事件。你是否思考過怎麼通過程式設計的方法知道會進入哪個事件呢?

其實這叫做預設事件(類似的,還有乙個預設屬性的概念),是乙個特性:defaulteventattribute

c# code

[defaultevent(

"click")]

class

control

click是應用於control類的預設事件,button則繼承了這個特性,而textboxbase這個類將這個特性修改為textchanged, textbox又從textboxbase繼承過來這個特性。

既然知道了原理,要去檢索,就很簡單了,直接反射就行了。另外,其實系統提供有專門的方法:

c# code

typedescriptor.getdefaultevent。

現在來看看treeview控制項的預設事件:

c# code

attribute attr

=attribute.getcustomattribute(

typeof

(treeview),

typeof

(defaulteventattribute));

defaulteventattribute de

=attr

asdefaulteventattribute;

messagebox.show(de.name);

或者 messagebox.show(typedescriptor.getdefaultevent(typeof(treeview)).name);

結果正是afterselect。

深入剖析C 的多型

一 什麼是多型 物件導向程式設計中的另外乙個重要概念是多型性。在執行時,可以通過指向基類的指標,來呼叫實現 派生類中的方法。可以把一組物件放到乙個陣列中,然後呼叫它們的方法,在這種場合下,多型性作用就體現出來了,這些物件不必是相同型別的物件。當然,如果 它們都繼承自某個類,你可以把這些派生類,都放到...

深入剖析C 的多型

天雨 一 什麼是多型 物件導向程式設計中的另外乙個重要概念是多型性。在執行時,可以通過指向基類的指標,來呼叫實現派生類中的方法。可以把一組物件放到乙個陣列中,然後呼叫它們的方法,在這種場合下,多型性作用就體現出來了,這些物件不必是相同型別的物件。當然,如果它們都繼承自某個類,你可以把這些派生類,都放...

C 深入剖析 委託設計

c 深入剖析 委託設計 程式 結論一 圖 一 實現的功能都是由圖 二 提供的類來完成的 結論二 兩者的建構函式和析構函式不存在任何關係 結論三 採用指標的方式,這樣左邊對外介面可以不發生改變。如果要改變某個功能只需要改變指標所對應的類 模式二 資料共享的模式 a b c 都是class string...