關於C#中的事件,園裡已經有大量的文章對其內在實現做過剖析,如果還不甚瞭解的可以閱讀這篇文章 通過Demo來細看C#事件的內在機制 雖然比較早,但非常清楚地展示了事件的內部機制,總結一下就是 1、事件在被編譯後生成了一個事件對應類型的私有委托,以及對應的_add方法和_remove方法用於該私有委托 ...
關於C#中的事件,園裡已經有大量的文章對其內在實現做過剖析,如果還不甚瞭解的可以閱讀這篇文章
通過Demo來細看C#事件的內在機制
雖然比較早,但非常清楚地展示了事件的內部機制,總結一下就是
1、事件在被編譯後生成了一個事件對應類型的私有委托,以及對應的_add方法和_remove方法用於該私有委托的註冊和取消註冊,其實就是平時常用的“+=”和“-=”。正是由於這個原因,所以事件在外部只能通過_add和_remove來對其調用鏈進行修改,而不能直接使用“=”,這使得事件的封裝性要優於委托(理論上特別符合觀察者模式)。
2、當我們通過_add和_remove來訂閱事件和取消訂閱的時候,最終改變的是生成的私有委托,而改變所使用的方法就是System.Delegate的Combine()。當事件第一次被訂閱時,會將null和被訂閱的方法進行Comine,然後將返回值賦值給私有委托。在之後事件被訂閱的時候,會將原有的委托和新的方法進行Combine,然後將返回值賦給原委托。(這裡非常像使用+對字元串進行拼接)
根據這個機制,不難想到一個問題,既然事件的訂閱和取消最終採用的是Combine方法,而Combine方法又是通過生成新的委托然後返回的方式來實現的,如果事件的訂閱者很多很多,又或者訂閱和取消操作進行得非常頻繁,是不是會影響到GC?答案是肯定的。
我做了一個簡單的測試
像上面這樣的代碼,Test方法中的GC達到了0.5m,雖然像這樣的代碼比較極端,但也充分說明瞭,如果一個事件被頻繁的訂閱和取消,這裡面產生的GC絕對是不容忽視的。就如同大家都知道不要大量和頻繁地使用“+”來拼接字元串一樣,對於事件,在訂閱列表會頻繁變化的時候,建議可以通過自行封裝一個委托列表來達到和事件類似的效果,通過列表的Add和Remove來添加和去除委托,那樣產生的GC基本就可以忽略不計了。