[AaronYang]C#人愛學不學8[事件和.net4.5的弱事件深入淺出]

來源:互聯網
上載者:User

標籤:

沒有偉大的願望,就沒有偉大的天才--Aaronyang的部落格(www.ayjs.net)-www.8mi.me
1. 事件-我的講法

老師常告訴我,事件是特殊的委託,為委託提供了一種發布/訂閱機制。

  1. 自訂事件:自訂一個類,繼承EventArgs
  2. 使用泛型委派EventHandler<T>,本質:public delegate void EventHandler<TEventArgs>(object sender,TEventArgs e) where  TEventHandlers:EventArgs
  3. 定義事件的簡單寫法    public event EventHandler<TEventArgs> myevent;這裡的EventHandler<TEventArgs>指的是符合2中的方法簽名形式,void,有兩個參數的,第一個sender參數包含事件的寄件者。第二個參數提供了事件的相關資訊。因為這個事件可以自訂,所以可以帶來很多豐富的資訊,也可以使用系統內建的。
  4. 使用事件屬性中的add和remove添加或者刪除委託的處理常式(事件),也可以自己使用+=或者-=處理
  5. 使用事件

1.1 我們來寫一個簡單的例子,讓你理解這幾個知識。

需求:我需要使用者使用我提供的類,可能就是個dll了,可以在自己的類中,定義事件的詳細內容。例如:部落格事件,有了1篇新文章,可能會有閱讀者去閱讀部落格,有的人使用電腦,有的人使用手機,有的人使用kindle

設計:

第一步:自訂事件,好處是可以在觸發事件時候提供其他的對象值

    public class BlogEventArgs : EventArgs {        /// <summary>        /// 部落格標題        /// </summary>        /// <param name="title"></param>        public BlogEventArgs(string title) {            this.Title = title;        }        //部落格標題        public string Title { get; set; }    }

第二步:定義個事件,方便使用者增加 自己的定義的事件,刪除自己的定義的事件,一個調用使用者綁定好的事件(特殊的委託)的方法

    public class BlogEventProvider    {        /// <summary>        /// 第一步:定義個事件,並封裝,給使用者綁定這個事件        /// </summary>        private event EventHandler<BlogEventArgs> blogEvents;        public event EventHandler<BlogEventArgs> BlogEvents        {            add            {                blogEvents += value;            }            remove            {                blogEvents -= value;            }        }        //觸發使用者綁定的事件,或者事件前加些處理,然後再觸發        public void PreviewReadBlog(string title) {            Console.WriteLine("正在擷取部落格《{0}》的內容...",title);            OnReadBlog(title);        }        protected virtual void OnReadBlog(string title) {            if (blogEvents != null) {                blogEvents(this, new BlogEventArgs(title));            }        }    }

第三步:定義個部落格支援的裝置枚舉

    /// <summary>    /// 部落格文章支援的裝置    /// </summary>    public enum BlogSupportDevice    {        PC,        Kindle,        Mobile    }

第四步:定義閱讀部落格的人,當然這裡的事件的實現,也可以寫在別處。

    /// <summary>    /// 部落格閱讀者    /// </summary>    public class BlogReader    {        public BlogReader(string readername, BlogSupportDevice device) {            this.ReaderName = readername;            this.Device = device;        }        public string ReaderName { get; set; }        public BlogSupportDevice Device { get; set; }        /// <summary>        /// 因為需要知道部落格的標題還有其他細節,所以自訂了一個BlogEventArgs類        /// </summary>        /// <param name="sender"></param>        /// <param name="e"></param>        public void OnReadBlog(object sender,BlogEventArgs e) {            Console.WriteLine("{3}:{0}使用了{1}閱讀了《{2}》",ReaderName,Device.ToString(),e.Title,DateTime.Now.ToLocalTime());        }    }

第五步:使用

第六步:拓展與反思

 1. 如果我綁定了N次相同的事件,然後執行事件,是只會觸發1次,還是觸發N次?

    答案:是N次,這也是上篇文章留的題目,委託被綁定多個相同的方法會執行多次嗎?

 2. 完整第五步代碼,使用-=移除上一個事件,不然執行事件時候,會執行多次,因為事件是特殊的委託,具有多播委託的特性。

        static void Main(string[] args)        {            //定義了事件提供者            BlogEventProvider provider = new BlogEventProvider();            //定義部落格閱讀人            BlogReader ay = new BlogReader("小aaronyang", BlogSupportDevice.PC);            //ay開始綁定自己定義的事件,事件提供者,也在觸發這個事件之前加了一些措施,而不是任由使用者自己搞的            provider.BlogEvents += ay.OnReadBlog;            //開始閱讀部落格            provider.PreviewReadBlog("[Aaronyang] 寫給自己的WPF4.5 筆記[2相依性屬性]");                  provider.BlogEvents -= ay.OnReadBlog;//移除ay閱讀者            //定義部落格閱讀人            BlogReader yy = new BlogReader("楊洋", BlogSupportDevice.Kindle);            //ay開始綁定自己定義的事件,事件提供者,也在觸發這個事件之前加了一些措施,而不是任由使用者自己搞的            provider.BlogEvents += yy.OnReadBlog;            //開始閱讀            provider.PreviewReadBlog("[Aaronyang] 寫給自己的WPF4.5 筆記[1布局]");            Console.ReadLine();        }

 

 2. 事件不複雜,就好比js中把方法當做一個參數傳遞,例如 function A(B,d){B()}

    這裡B是一個方法,人家調用A時候就可以自訂一個B,而B只是一個function對象,具有幾個參數,在A函數的參數中未體現,但是在調用B時候就有體現了。假設B函數具有2個參數(x,y),那麼

     function A(B,d){ var e=B(1,3)}

     function cusB(x,y){return x*y}

     A(cusB,‘自訂‘)

     說白了,感覺有點像給使用者留事件介面

 

===============aaronyang========www.8mi.me================== 2.弱事件-引入

理解事件的發布和訂閱模型-publisher和 listener

aaronyang大話講解:publisher就是定義事件的地方,如上例就是BlogEventProvider類,它裡面定義了private event EventHandler<BlogEventArgs> blogEvents;

而Listener就是給這個事件 添加或者刪除具體事件實現的地方。如上例就是BlogReader類,它裡面有blogEvents符合事件(blogEvents)的方法簽名。因此就可以正常用+=和-=委託的東東了。

 

OK,下面我們來測試為什麼需要弱事件?對的,就是防止事件的記憶體泄露問題。

接下來,我們在Program類中加一個記憶體回收方法,方便測試

  static void TriggerGC()        {            Console.WriteLine("Starting GC.");            GC.Collect();            GC.WaitForPendingFinalizers();            GC.Collect();            Console.WriteLine("GC finished.");        } 

第一個 GC.Collect() 觸發.net的CLR垃圾收集器,對於負責清理不再使用的對象,和那些類中沒有終結器(即c#中的解構函式)的對象,CLR垃圾收集器足夠勝任

GC.WaitForPendingFinalizers() 等待其他對象的終結器執行;

第二個GC.Collect() 確保新產生的對象也被清理了

 

接下來,我們接著上一個例子繼續寫,上面第五步的代碼假設你有了。

我們現在把ay對象置空,接著讓記憶體回收ay對象,然後我們執行事件

      static void Main(string[] args)        {            //定義了事件提供者            BlogEventProvider provider = new BlogEventProvider();            //定義部落格閱讀人            BlogReader ay = new BlogReader("小aaronyang", BlogSupportDevice.PC);            //ay開始綁定自己定義的事件,事件提供者,也在觸發這個事件之前加了一些措施,而不是任由使用者自己搞的            provider.BlogEvents += ay.OnReadBlog;            //開始閱讀部落格            provider.PreviewReadBlog("[Aaronyang] 寫給自己的WPF4.5 筆記[2相依性屬性]");            //provider.BlogEvents -= ay.OnReadBlog;//移除ay閱讀者            ////定義部落格閱讀人            //BlogReader yy = new BlogReader("楊洋", BlogSupportDevice.Kindle);            ////ay開始綁定自己定義的事件,事件提供者,也在觸發這個事件之前加了一些措施,而不是任由使用者自己搞的            //provider.BlogEvents += yy.OnReadBlog;            ////開始閱讀            //provider.PreviewReadBlog("[Aaronyang] 寫給自己的WPF4.5 筆記[1布局]");                        //測試事件的強引用 ay            ay = null;            TriggerGC();            provider.PreviewReadBlog("[Aaronyang] 寫給自己的WPF4.5 筆記[3進階事件]");                    Console.ReadLine();        }

結果:

結果驚訝的發現:事件還能被執行!這就說明ay對象還在啊,這就是強引用,所以好多C#寫代碼都在執行完了事件後,就接著寫其他的代碼了,也不管記憶體回收能不能回收,導致了記憶體泄露的問題。

接著,我們再在 BlogEventProvider中增加一個解構函式,確認BlogEventProvider對象是否真的被回收

 ~BlogEventProvider(){            Console.WriteLine("BlogEventProvider的解構函式被執行");        }

OK,我們再在Program的Main方法中增加代碼:

            provider = null;            TriggerGC();

效果:

OK,我確定此時ay對象被釋放了。當然不一定要去釋放ay對象才能回收記憶體啊,事件的 記憶體泄露問題,你只要+=後,執行完,手動去-=事件就行了。這樣,記憶體回收行程在後台也就可以自動回收ay對象了,不然,你一直在使用provider對象時候,記憶體回收永遠都不能回收ay,假設provider中事件綁定了很多事件實現,而這些實現,你執行了,但是都沒有做-=,那麼就會導致能存泄露的問題,當然也就是記憶體高佔用的問題。好吧,假如你想再new一個provider,那個廢棄,那你new的太多的話,也會很多記憶體,而你如果沒有手動GC,那記憶體佔用更恐怖。

            provider.BlogEvents -= ay.OnReadBlog;            ay = null;            TriggerGC();            provider.PreviewReadBlog("[Aaronyang] 寫給自己的WPF4.5 筆記[3進階事件]");

那麼這個問題,怎麼解決?.net4.5提供了一個更直接的方法,弱引用,當然4.5以前也有弱引用的其他方式,當然比較複雜,這裡不講了,只講4.5的方式。

 

3.弱事件-aaronyang開講

如果用4.5的泛型模式,則寫法超簡單,再也不用繼承WeakEventManager,IWeakEventListener了。MSDN位置:查看

而且再也不用擔心綁定多次同一個事件,被執行多次了

我們使用

          System.Windows.WeakEventManager<BlogEventProvider, BlogEventArgs>.AddHandler(provider, "BlogEvents", ay.OnReadBlog);

替代剛才的

provider.BlogEvents += ay.OnReadBlog;

前提:你要引入WindowsBase.dll

  static void Main(string[] args)        {            //定義了事件提供者            BlogEventProvider provider = new BlogEventProvider();            //定義部落格閱讀人            BlogReader ay = new BlogReader("小aaronyang", BlogSupportDevice.PC);            //ay開始綁定自己定義的事件,事件提供者,也在觸發這個事件之前加了一些措施,而不是任由使用者自己搞的            //provider.BlogEvents += ay.OnReadBlog;            System.Windows.WeakEventManager<BlogEventProvider, BlogEventArgs>.AddHandler(provider, "BlogEvents", ay.OnReadBlog);            //測試綁定多次,也只會執行一次            //System.Windows.WeakEventManager<BlogEventProvider, BlogEventArgs>.AddHandler(provider, "BlogEvents", ay.OnReadBlog);            //開始閱讀部落格            provider.PreviewReadBlog("[Aaronyang] 寫給自己的WPF4.5 筆記[2相依性屬性]");            ay = null;            TriggerGC();            provider.PreviewReadBlog("[Aaronyang] 寫給自己的WPF4.5 筆記[3進階事件]");            Console.ReadLine();        }

效果:

發現第二次執行事件時候,ay已經被釋放了。真TM爽!!

 

======安徽六安=========www.ayjs.net==========aaronyang========楊洋========www.8mi.me==========

 

[AaronYang]C#人愛學不學8[事件和.net4.5的弱事件深入淺出]

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.