日本好好热aⅴ|国产99视频精品免费观看|日本成人aV在线|久热香蕉国产在线

  • <cite id="ikgdy"><table id="ikgdy"></table></cite>
    1. 西西軟件園多重安全檢測(cè)下載網(wǎng)站、值得信賴的軟件下載站!
      軟件
      軟件
      文章
      搜索

      首頁編程開發(fā)C#.NET → C#事件具體實(shí)現(xiàn)步驟

      C#事件具體實(shí)現(xiàn)步驟

      相關(guān)軟件相關(guān)文章發(fā)表評(píng)論 來源:百度搜索時(shí)間:2012/11/14 22:49:15字體大。A-A+

      作者:西西點(diǎn)擊:948次評(píng)論:0次標(biāo)簽: 事件

      • 類型:生活服務(wù)大小:4.1M語言:中文 評(píng)分:10.0
      • 標(biāo)簽:
      立即下載

      定義一個(gè)事件成員,表示該類型提供了如下功能:

      1.能夠在事件中注冊(cè)方法 2.能夠在事件中注銷方法 3.當(dāng)事件發(fā)生時(shí),注冊(cè)的方法會(huì)被通知

      (事件內(nèi)部維護(hù)了一個(gè)注冊(cè)方法列表)

      CLR的事件模型是基于委托的,它可以通過類型安全的方式調(diào)用回調(diào)方法。而回調(diào)方法是訂閱事件的對(duì)象接收通知的方式。通過一個(gè)例子來說明:

      ①Fax對(duì)象的方法注冊(cè)到MailManager事件 ②Pager對(duì)象的方法注冊(cè)到MailManager事件 ③新的郵件到達(dá)MailManager ④MailManager對(duì)象向注冊(cè)的方法發(fā)出通知,接收通知的方法可以隨意處理。

      具體實(shí)現(xiàn)步驟如下:

      1.定義一個(gè)類型,能夠hold住任何發(fā)送到事件通知接收者的信息

      當(dāng)一個(gè)事件被觸發(fā),觸發(fā)事件的對(duì)象可能希望發(fā)送一些額外的信息給事件通知的接收對(duì)象。這些額外的信息需要封裝在它自己的類中,根據(jù)約定該類需要從System.EventArgs類派生,并且命名以EventArgs結(jié)尾。這里定義一個(gè)NewMailEventArgs類:


          public class NewMailEventArgs : EventArgs
          {
              private readonly String m_from, m_to, m_subject;
              public NewMailEventArgs(String from, String to, String subject)
              {
                  m_from = from;
                  m_to = to;
                  m_subject = subject;
              }
      
              public String From { get { return m_from; } }
              public String To { get { return m_to; } }
              public String Subject { get { return m_subject; } }
      
          }



      關(guān)于EventArgs


      [ComVisible(true)]
      [Serializable]
      public class EventArgs
      {
          public readonly static EventArgs Empty;
      
          static EventArgs()
          {
              EventArgs.Empty = new EventArgs();
          }
      
          public EventArgs()
          {
          }
      }


      這個(gè)類沒有實(shí)際的用途,只是作為一個(gè)基類讓其他對(duì)象繼承。很多對(duì)象不需要傳遞額外的信息,例如按鈕事件,只是調(diào)用一個(gè)回調(diào)方法就夠了。當(dāng)我們定義的事件不需要傳遞額外的信息時(shí),這時(shí)調(diào)用EventArgs.Empty就行了,不需要重新構(gòu)建一個(gè)EventArgs對(duì)象。

      2.定義事件成員

          public class MailManager
          {
              ...
              //NewMail事件名,
              //EventHanlder<NewMailEventArgs>,所有的事件通知接收對(duì)象必須提供給該委托類型匹配的回調(diào)方法
              public event EventHandler<NewMailEventArgs> NewMail;
          }


      System.EventHandler委托的定義為:public delegate void EventHandler<TEventArgs>(Object sender, TEventArgs e) where TEventArgs: EventArgs;

      為什么這里第一個(gè)參數(shù)sender的類型是Object?畢竟MailManager類型是唯一觸發(fā)這個(gè)事件的,所以可以設(shè)計(jì)成這樣:
      void MethodName(MailManager sender,NewMailEventArgs e)
      這種情況會(huì)有一個(gè)弊端,當(dāng)sender是SmtpMailManager時(shí),回調(diào)方法也需要改變,使用Object能夠很好的兼容。定義回調(diào)方法的參數(shù)名約定為e,這樣做主要是為了保持一致性。方便開發(fā)人員。

      事件機(jī)制要求所有的事件處理方法必須返回void,這是必要的,因?yàn)橐粋(gè)事件可能觸發(fā)很多的回調(diào)方法,沒有辦法獲取所有的返回值,索性就不允許返回值,全部為void。有些FCL里面的事件處理程序沒有遵循,而是返回了一個(gè)Assembly類型。

      3.定義一個(gè)方法來響應(yīng)事件的發(fā)生

      按照慣例,這個(gè)類應(yīng)該定義一個(gè)protected,virtual的方法供內(nèi)部的代碼調(diào)用。這個(gè)方法接收一個(gè)NewMailEventArgs對(duì)象,這個(gè)對(duì)象包含要傳遞給消息接收方的一些信息。如下:

              protected virtual void OnNewMail(NewMailEventArgs e)
              {
                  //復(fù)制一個(gè)委托的引用到臨時(shí)字段temp,這樣確保線程安全
                  EventHandler<NewMailEventArgs> temp = Interlocked.CompareExchange(ref NewMail, null, null);

                  //任何注冊(cè)到事件里面的方法,通知它們
                  if (temp != null)
                  {
                      temp(this, e);
                  }
              }


      Tips:使用線程安全的方式觸發(fā)事件(①——>④為不斷改進(jìn)的過程)

      ①當(dāng).NET第一次推出的時(shí)候,給開發(fā)者推薦的事件觸發(fā)方式如下:

      //v1.0
      protected virtual void OnNewMail(NewMailEventArgs e)
      {
           if (NewMail != null)
           {
                NewMail(this, e);   
           } 
      }


      弊端:這里檢查了NewMail不為null才觸發(fā),但是當(dāng)檢查完之后,在調(diào)用NewMail之前,有其他的線程從委托鏈中移除了一個(gè)委托,使得NewMail為null,此時(shí)會(huì)拋出異常。

      ②先將NewMail用一個(gè)臨時(shí)變量存起來,這時(shí)就不會(huì)因?yàn)檎{(diào)用時(shí)被其他線程修改而拋出異常。之所以能夠這樣做,是因?yàn)槲蓄愋透址愋鸵粯邮遣豢勺兊摹?/p>

      //v2.0
      protected void OnNewMail(NewMailEventArgs e)

           EventHandler<NewMailEventArgs> temp = NewMail;
           if (temp != null)
           {
                temp(this, e); 
           } 
      }


      弊端:可能被編譯器優(yōu)化掉本地temp變量,如果發(fā)生這種情況,就回到了第一種了。

      ③修復(fù)上面的bug,如下:

      //v3.0
      protected void OnNewMail(NewMailEventArgs e)

            EventHandler<NewMailEventArgs> temp = Thread.VolatileRead(ref NewMail);
            if (temp != null)
            {
                 temp(this, e);
            }
      }


      這里使用VolatileRead會(huì)強(qiáng)制讀取temp的值,但是這里不能這樣寫,編譯不通過。但是有一個(gè)Interlocked.CompareExchange可以使用:

              //v4.0       
              protected virtual void OnNewMail(NewMailEventArgs e)
              {
                  //復(fù)制一個(gè)委托的引用到臨時(shí)字段temp,這樣確保線程安全
                  EventHandler<NewMailEventArgs> temp = Interlocked.CompareExchange(ref NewMail, null, null);

                  //任何注冊(cè)到事件里面的方法,通知它們
                  if (temp != null)
                  {
                      temp(this, e);
                  }
              }


      如果NewMail為null,CompareExchange將NewMail的值改變?yōu)閚ull,如果不為null則返回原值。換句話說,CompareExchange不會(huì)改變NewMail的值,只是以線程安全的方式返回NewMail的值,這里是一個(gè)原子操作。

      第④個(gè)版本是最佳的,技術(shù)上最正確的版本。實(shí)際開發(fā)中還是可以使用第②個(gè)版本,因?yàn)镴IT編譯器能夠識(shí)別這種模式而不去優(yōu)化本地的temp變量。特別地,所有微軟的JIT編譯器都遵循不會(huì)對(duì)堆引入新的讀取,因此緩存一個(gè)引用在本地變量可以確保堆引用只被訪問一次(這是沒有寫入文檔的,理論上,還是可能發(fā)生變化,所以最好選用第④版本。)

      為了方便可以定義一個(gè)擴(kuò)展方法來封裝:

          public static class EventArgExtensions
          {
              public static void Raise<TEventArgs>(this TEventArgs e, Object sender, ref EventHandler<TEventArgs> eventDelegate)
      where TEventArgs : EventArgs
              {
                  EventHandler<TEventArgs> temp = Interlocked.CompareExchange(ref eventDelegate, null, null);
                  if (temp != null)
                  {
                      temp(sender, e);
                  }
              }
          }


      然后可以重寫OnNewMail:

              protected virtual void OnNewMail(NewMailEventArgs e)
              {
                   e.Raise(this, ref NewMail);
              }


      4.定義一個(gè)方法用來傳遞一些輸入到事件

              public void SimulateNewMail(String from, String to, String subject)
              {
                  NewMailEventArgs e = new NewMailEventArgs(from, to, subject);
                  OnNewMail(e);
              }


      編譯器是怎么實(shí)現(xiàn)事件的?

      在MailManager里面用一行定義了一個(gè)事件:public event EventHandler<NewMailEventArgs> NewMail;
      編譯器會(huì)將上面一行代碼編譯為三個(gè)部分如下:

      // 1. 初始化一個(gè)私有的委托變量
      private EventHandler<NewMailEventArgs> NewMail = null;  

      // 2. 將方法注冊(cè)到事件的public方法add_Xxx  (Xxx表示事件名)  
      public  void add_NewMail(EventHandler<NewMailEventArgs> value) 

         // 循環(huán)和調(diào)用CompareExchange是一種添加委托的線程安全的方式
         EventHandler<NewMailEventArgs> prevHandler;
         EventHandler<NewMailEventArgs> newMail = this.NewMail;
         do {
            prevHandler = newMail; 
            EventHandler<NewMailEventArgs>newHandler = 
               (EventHandler<NewMailEventArgs>) Delegate.Combine(prevHandler, value);
            newMail = Interlocked.CompareExchange<EventHandler<NewMailEventArgs>>( 
               ref this.NewMail, newHandler, prevHandler);
         } while (newMail != prevHandler); 

      // 3. public方法remove_Xxx(Xxx表示方法名)  
      // 允許方法從事件中注銷public void remove_NewMail(EventHandler<NewMailEventArgs> value) 

         EventHandler<NewMailEventArgs> prevHandler; 
         EventHandler<NewMailEventArgs> newMail = this.NewMail;
         do {
            prevHandler = newMail; 
            EventHandler<NewMailEventArgs> newHandler = 
               (EventHandler<NewMailEventArgs>) Delegate.Remove(prevHandler, value); 
            newMail = Interlocked.CompareExchange<EventHandler<NewMailEventArgs>>( 
               ref this.NewMail, newHandler, prevHandler);
         } while (newMail != prevHandler); 
      }


      從這里可以看出事件的確是一塊語法糖,這里首先定義了一個(gè)私有的委托變量,接著是對(duì)該對(duì)象增加add和remove操作。所以事件是對(duì)委托的封裝,來限制我們只能對(duì)委托進(jìn)行add和remove操作,外界并不能訪問委托變量本身(私有的)。

      注:

      1.如果Remove一個(gè)未添加的方法,Delegate.Remove在內(nèi)部不會(huì)執(zhí)行任何操作,也不會(huì)拋異常,事件的方法集合不會(huì)發(fā)送變化。

      2.在這個(gè)里面add和remove方法都是public的,原因是定義event NewMail時(shí)是public的,它們的可訪問性保持一致。Event成員也可以被定義為static或virtual,此時(shí),編譯器生成的add和remove方法也是static或virtual

      3.除了上面列舉的三個(gè)部分,編譯器還會(huì)生成一個(gè)事件定義的入口。該入口包含一些標(biāo)志位和在事件下的委托類型,以及add和remove訪問器的引用。這個(gè)信息能夠簡(jiǎn)單的描述抽象的事件概念和訪問器方法的聯(lián)系。

      定義一個(gè)類型來監(jiān)聽事件

         public sealed class Fax
          {
              public Fax(MailManager mm)
              {
                  // 注冊(cè)回調(diào)方法FaxMsg
                  mm.NewMail += FaxMsg;
              }

              // 當(dāng)NewEmail發(fā)生時(shí),觸發(fā)這個(gè)方法執(zhí)行
              private void FaxMsg(Object sender, NewMailEventArgs e)
              {
                  // 'sender'用來識(shí)別是MailManager對(duì)象
                  // 'e' MailManager要傳遞的額外的事件信息
                  Console.WriteLine("Faxing mail message:");
                  Console.WriteLine("   From={0}, To={1}, Subject={2}",
                     e.From, e.To, e.Subject);
              }

              //從事件從注銷方法,以后不在收到通知
              public void Unregister(MailManager mm)
              {
                  mm.NewMail -= FaxMsg;
              }
          }


      C#內(nèi)置對(duì)事件的支持,編譯會(huì)根據(jù)+=生成代碼:mm.add_NewMail(new EventHandler<NewMailEventArgs>(this.FaxMsg));

      編譯器構(gòu)造了一個(gè)EventHandler<NewMailEventsArgs>委托對(duì)象來封裝Fax類的FaxMsg方法。然后,C#編譯器調(diào)用MailManager的add_NewMail方法,將它傳遞到新的委托。IL如下:

          .event class [mscorlib]System.EventHandler`1<class TypeTest.NewMailEventArgs> NewMail
          {
              .addon instance void TypeTest.MailManager::add_NewMail(class [mscorlib]System.EventHandler`1<class TypeTest.NewMailEventArgs>)
              {
                  .locals init (
                      [0] class [mscorlib]System.EventHandler`1<class TypeTest.NewMailEventArgs> V_0,
                      [1] class [mscorlib]System.EventHandler`1<class TypeTest.NewMailEventArgs> V_1,
                      [2] class [mscorlib]System.EventHandler`1<class TypeTest.NewMailEventArgs> V_2,
                      [3] bool V_3
                  )

                  IL_0000: ldarg.0
                  IL_0001: ldfld class [mscorlib]System.EventHandler`1<class TypeTest.NewMailEventArgs> TypeTest.MailManager::NewMail
                  IL_0006: stloc.0
                  .loop
                  {
                      IL_0007: ldloc.0
                      IL_0008: stloc.1
                      IL_0009: ldloc.1
                      IL_000a: ldarg.1
                      IL_000b: call class [mscorlib]System.Delegate [mscorlib]System.Delegate::Combine(class [mscorlib]System.Delegate,  class [mscorlib]System.Delegate)
                      IL_0010: castclass class [mscorlib]System.EventHandler`1<class TypeTest.NewMailEventArgs>
                      IL_0015: stloc.2
                      IL_0016: ldarg.0
                      IL_0017: ldflda class [mscorlib]System.EventHandler`1<class TypeTest.NewMailEventArgs> TypeTest.MailManager::NewMail
                      IL_001c: ldloc.2
                      IL_001d: ldloc.1
                      IL_001e: call class [mscorlib]System.EventHandler`1<class TypeTest.NewMailEventArgs> [mscorlib]System.Threading.Interlocked::CompareExchange<class [mscorlib]System.EventHandler`1<class TypeTest.NewMailEventArgs>>(!!0&,  !!0,  !!0)
                      IL_0023: stloc.0
                      IL_0024: ldloc.0
                      IL_0025: ldloc.1
                      IL_0026: ceq
                      IL_0028: ldc.i4.0
                      IL_0029: ceq
                      IL_002b: stloc.3
                      IL_002c: ldloc.3
                      IL_002d: brtrue.s IL_0007
                  }
                  IL_002f: ret
              }
              .removeon instance void TypeTest.MailManager::remove_NewMail(class [mscorlib]System.EventHandler`1<class TypeTest.NewMailEventArgs>)
              {
                  .locals init (
                      [0] class [mscorlib]System.EventHandler`1<class TypeTest.NewMailEventArgs> V_0,
                      [1] class [mscorlib]System.EventHandler`1<class TypeTest.NewMailEventArgs> V_1,
                      [2] class [mscorlib]System.EventHandler`1<class TypeTest.NewMailEventArgs> V_2,
                      [3] bool V_3
                  )

                  IL_0000: ldarg.0
                  IL_0001: ldfld class [mscorlib]System.EventHandler`1<class TypeTest.NewMailEventArgs> TypeTest.MailManager::NewMail
                  IL_0006: stloc.0
                  .loop
                  {
                      IL_0007: ldloc.0
                      IL_0008: stloc.1
                      IL_0009: ldloc.1
                      IL_000a: ldarg.1
                      IL_000b: call class [mscorlib]System.Delegate [mscorlib]System.Delegate::Remove(class [mscorlib]System.Delegate,  class [mscorlib]System.Delegate)
                      IL_0010: castclass class [mscorlib]System.EventHandler`1<class TypeTest.NewMailEventArgs>
                      IL_0015: stloc.2
                      IL_0016: ldarg.0
                      IL_0017: ldflda class [mscorlib]System.EventHandler`1<class TypeTest.NewMailEventArgs> TypeTest.MailManager::NewMail
                      IL_001c: ldloc.2
                      IL_001d: ldloc.1
                      IL_001e: call class [mscorlib]System.EventHandler`1<class TypeTest.NewMailEventArgs> [mscorlib]System.Threading.Interlocked::CompareExchange<class [mscorlib]System.EventHandler`1<class TypeTest.NewMailEventArgs>>(!!0&,  !!0,  !!0)
                      IL_0023: stloc.0
                      IL_0024: ldloc.0
                      IL_0025: ldloc.1
                      IL_0026: ceq
                      IL_0028: ldc.i4.0
                      IL_0029: ceq
                      IL_002b: stloc.3
                      IL_002c: ldloc.3
                      IL_002d: brtrue.s IL_0007
                  }
                  IL_002f: ret
              }
          }


      當(dāng)MailManager對(duì)象觸發(fā)了事件,F(xiàn)ax對(duì)象的FaxMsg方法會(huì)被調(diào)用,這個(gè)方法的一個(gè)引用會(huì)被作為MailManager對(duì)象的第一個(gè)參數(shù)sender傳遞給MailManager。多數(shù)時(shí)候該參數(shù)被忽略。當(dāng)Fax對(duì)象想訪問MailManager對(duì)象來響應(yīng)事件通知時(shí)會(huì)使用。第二個(gè)參數(shù)是NewMailEventArgs對(duì)象,傳遞額外的信息。

      當(dāng)一個(gè)對(duì)象不再希望接收某事件的通知時(shí),應(yīng)該從事件里面注銷。只要某一個(gè)對(duì)象的任何一個(gè)方法還在事件里面,這個(gè)對(duì)象就不會(huì)被GC回收。如果類型實(shí)現(xiàn)了IDisposable的Dispose方法,該實(shí)現(xiàn)應(yīng)該注銷所有在事件里面的方法。

        相關(guān)評(píng)論

        閱讀本文后您有什么感想? 已有人給出評(píng)價(jià)!

        • 8 喜歡喜歡
        • 3 頂
        • 1 難過難過
        • 5 囧
        • 3 圍觀圍觀
        • 2 無聊無聊

        熱門評(píng)論

        最新評(píng)論

        發(fā)表評(píng)論 查看所有評(píng)論(0)

        昵稱:
        表情: 高興 可 汗 我不要 害羞 好 下下下 送花 屎 親親
        字?jǐn)?shù): 0/500 (您的評(píng)論需要經(jīng)過審核才能顯示)