<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      多態(tài)與 new [C#]

      多態(tài)與 new [C#]

       

      Written by Allen Lee

       

      0. 浪子 《今天你多態(tài)了嗎?》 提出兩個這樣的 問題

      • 1) “使用基類繼承多態(tài),有一點特別需要注意的就是:基類(抽象或者非抽象)中需要獲得多態(tài)效果的成員必須有 abstractvirtual 修飾。”使用 new 來重寫的成員不能形成多態(tài)嗎?
      • 2) “多態(tài)就是使得你能夠用一種統(tǒng)一的方式來處理一組各具個性卻同屬一族的不同個體的機制。” new 重寫后的成員是否符合了這個范疇?

       

      1. 你通常怎樣用多態(tài)?

              假設(shè)我有一個類,里面有一個 PrintStatus 方法,用于打印實例的當(dāng)前狀態(tài),我希望該類的派生類都帶有一個 PrintStatus 方法,并且這些方法都用于打印其實例的當(dāng)前狀態(tài)。那么我會這樣表達我的愿望:

      //  Code #01

      class  Base
      {
          
      public   virtual   void  PrintStatus()
          
      {
              Console.WriteLine(
      " public virtual void PrintStatus() in Base " );
          }

      }

      于是我可以寫一個這樣的方法:

      //  Code #02

      public   void  DisplayStatusOf(Base[] bs)
      {
          
      foreach  (Base b  in  bs)
          
      {
              b.PrintStatus();
          }

      }

      bs 中可能包含著不同的 Base 的派生類,但我們卻可以忽略這些“個性”而使用一種統(tǒng)一的方式來處理某事。在 .NET 2.0 中,XmlReader 的 Create 有這樣一個版本:

      public   static  XmlReader Create(Stream input);

      你可以向 Create 傳遞任何可用的“流”,例如來自文件的“流”(FileStream)、來自內(nèi)存的“流”(MemoryStream)或來自網(wǎng)絡(luò)的“流”(NetworkStream)等。雖然每一中“流”的工作細節(jié)都不同,但我們卻使用一種統(tǒng)一的方式來處理這些“流”。

       

      2. 假如有人不遵守承諾...

              DisplayStatusOf 隱含著這樣一個假設(shè):bs 中如果存在派生類的實例,那么該派生類應(yīng)該重寫 PrintStatus,當(dāng)然必須加上 override 關(guān)鍵字:

      //  Code #03

      class  Derived1 : Base
      {
          
      public   override   void  PrintStatus()
          
      {
              Console.WriteLine(
      " public override void PrintStatus() in Derived1 " );
          }

      }

      你可以把這看作一種承諾、約定,直到有人沉不住氣...

      //  Code #04

      class  Derived2 : Base
      {
          
      public   new   void  PrintStatus()
          
      {
              Console.WriteLine(
      " public new void PrintStatus() in Derived2 " );
          }

      }

      假設(shè)我們有這樣一個數(shù)組:

      //  Code #05

      Base[] bs 
      =   new  Base[]
      {
          
      new  Base(),
          
      new  Derived1(),
          
      new  Derived2()
      }
      ;

      把它傳遞給 DisplayStatusOf,則輸出是:

      //  Output #01

      //  public virtual void PrintStatus() in Base
      //  public override void PrintStatus() in Derived1
      //  public virtual void PrintStatus() in Base

      從輸出結(jié)果中很容易看出 Derived2 并沒有按照我們期望的去做。但你無需驚訝,這是由于 Derived2 的設(shè)計者沒有“遵守約定”的緣故。

       

      3. new:封印咒術(shù)

              new 似乎給人一種這樣的感覺,它的使用者喜歡打破別人的約定,然而,如果使用恰當(dāng),new 可以彌補基類設(shè)計者的“短見”。在 Creating a Data Bound ListView Control 中,Rockford Lhotka 就示范了如何封印原來的 ListView.Columns,并使自行添加的返回 DataColumnHeaderCollection 的 Columns 取而代之。

              從 Output #01 中我們可以看到,new 只是把 Base.PrintStatus 封印起來而不是消滅掉,你可以解除封印然后進行訪問。對于 Derived2 的使用者,解封的方法是把 Derived2 的實例轉(zhuǎn)換成 Base 類型:

      //  Code #06

      Base d2 
      =   new  Derived2();
      d2.PrintStatus();

      //  Output #02

      //  public virtual void PrintStatus() in Base

      而在 Derived2 內(nèi)部,你可以透過 base 來訪問:

      //  Code #07

      base .PrintStatus();

      這種方法是針對實例成員的,如果被封印的成員是靜態(tài)成員的話,就要透過類名來訪問了。

       

      4. 假如 Base.PrintStatus 是某個接口的隱式實現(xiàn)...

              假如 Base 實現(xiàn)了一個 IFace 接口:

      //  Code #08

      interface  IFace
      {
          
      void  PrintStatus();
      }


      class  Base : IFace
      {
          
      public   virtual   void  PrintStatus()
          
      {
              Console.WriteLine(
      " public virtual void PrintStatus() in Base " );
          }

      }

      我們只需要讓 Derived2 重新實現(xiàn) IFace:

      //  Code #09

      class  Derived2 : Base, IFace
      {
          
      public   new   void  PrintStatus()
          
      {
              Console.WriteLine(
      " public new void PrintStatus() in Derived2 " );
          }

      }

      Derived1 保持不變。則把:

      //  Code #10

      IFace[] fs 
      =   new  IFace[]
      {
          
      new  Base(),
          
      new  Derived1(),
          
      new  Derived2(),
      }

      傳遞給:

      //  Code #11

      public   void  DisplayStatusOf(IFace[] fs)
      {
          
      foreach  (IFace f  in  fs)
          
      {
              f.PrintStatus();
          }

      }

      的輸出結(jié)果是:

      //  Output #03

      //  public virtual void PrintStatus() in Base
      //  public override void PrintStatus() in Derived1
      //  public new void PrintStatus() in Derived2

             從輸出結(jié)果中,我們可以看到,雖然 Derived2.PrintStatus 應(yīng)用了 new,但卻依然參與動態(tài)綁定,這是由于 new 只能割斷 Derived2.PrintStatus 和 Base.PrintStatus 的聯(lián)系,而不能割斷它與 IFace.PrintStatus 的聯(lián)系。我在 Derived2 的定義中重新指定實現(xiàn) IFace,這將使得編譯器認為 Derived2.PrintStatus 是 IFace.PrintStatus 的隱式實現(xiàn),于是,在動態(tài)綁定時 Derived2.PrintStatus 就被包括進來了。

       

      5. 誰的問題?

              我必須指出,如果 Base(Code #01)和 Derived2(Code #04)同時存在的話,它們倆其中一個存在著設(shè)計上的問題。為什么這樣說呢?Base 的設(shè)計者在 PrintStatus 上應(yīng)用 virtual 說明了他希望派生類能透過重寫這一方法來參與動態(tài)綁定,即多態(tài)性;而 Derived2 的設(shè)計者在 PrintStatus 上應(yīng)用 new 則說明了他希望割斷 Derived2.PrintStatus 和 Base.PrintStatus 之間的聯(lián)系,這將使得 Derived2.PrintStatus 無法參與到 Base 的設(shè)計者所期望的動態(tài)綁定中。如果在 Base.PrintStatus 上應(yīng)用 virtual(即對多態(tài)性的期望)是合理的話,那么 Derived2.PrintStatus 應(yīng)該換用另外一個名字了;如果在 Derived2.PrintStatus 上應(yīng)用 new(即否決參與動態(tài)綁定)是合理的,那么 Base.PrintStatus 應(yīng)該考慮是否去掉 virtual 了,否則就會出現(xiàn)一些奇怪的行為,例如 Output #01 的第三行輸出。

              假如繼承體系中多態(tài)性行為的期望是合理的話,那么更實際的做法應(yīng)該是把 Base 定義成這樣:

      //  Code #12

      abstract   class  Base
      {
          
      public   abstract   void  PrintStatus();
      }

      而原來 Base 中的實現(xiàn)應(yīng)該下移到一個派生類中:

      //  Code #13

      class  Derived3 : Base
      {
          
      public   override   void  PrintStatus()
          
      {
              Console.WriteLine(
      " public override void PrintStatus() in Derived3 [originally implemented in Base] " );
          }

      }

      這樣,Derived2.PrintStatus 將使得編譯無法完成,從而迫使其設(shè)計者要么更改方法的名字,要么換用 override 修飾。這種強制使得 Derived2 的設(shè)計者不得不重新考慮其設(shè)計的合理性。

              假如繼承體系中多態(tài)性行為的期望不總是合理呢?例如 Stream 有這樣一個方法:

      public   abstract   long  Seek( long  offset, SeekOrigin origin);

      現(xiàn)在假設(shè)我有一個方法在處理輸入流時需要用到 Stream.Seek:

      //  Code #14

      public   void  Resume(Stream input,  long  offset)
      {
          
      //  

          input.Seek(offset, SeekOrigin.Begin);

          
      //  
      }

      當(dāng)我們向 Resume 傳遞一個 NetworkStream 的實例,Resume 將會拋出一個 NotSupportedException,因為 NetworkStream 不支持 Seek。那么這是否說明 Stream 的設(shè)計有問題呢?

      設(shè)想 Resume 是一個下載工具進行斷點續(xù)傳的方法,然而,并不是所有的服務(wù)器都支持?jǐn)帱c續(xù)傳的,于是,你需要首先判斷輸入流是否支持 Seek 操作,再決定如何處理輸入流:

      //  Code #15

      public   void  Resume(Stream input,  long  offset)
      {
          
      if  (input.CanSeek)
          
      {
              
      //  

              input.Seek(offset, SeekOrigin.Begin);

              
      //  
          }

          
      else
          
      {
              
      //  
          }

      }

      如果 CanSeek 為 false,那就只好從頭來過了。

              實際上,我們并不能保證任何 Stream 的派生類都能夠支持某個(些)操作,我們甚至不能保證來自同一個派生類的所有實例都支持某個(些)操作。你可以設(shè)想有這樣一個 PriorityStream,它能夠根據(jù)當(dāng)前登錄賬號的權(quán)限來決定是否提供寫操作,這使得擁有足夠權(quán)限的人才能修改數(shù)據(jù)。或許 Stream 的設(shè)計者已經(jīng)預(yù)料到這類情況的發(fā)生,所以 CanRead、CanSeek 和 CanWrite 就被加入到 Stream 里了。

              值得注意的是,Code #07 的 Derived2 可能是一個很糟糕的設(shè)計,也可能是一個很實用的設(shè)計。在本文,它是一個很糟糕的設(shè)計,如果你足夠細心,你會察覺到 Derived2 的設(shè)計者希望 Derived2.PrintStatus 繞過 Base.PrintStatus 而直接和 IFace.PrintStauts 進行關(guān)聯(lián),表面上這沒什么不妥,但實質(zhì)上 Base.PrintStatus 和 IFace.PrintStauts 在約定上是同質(zhì)的,這意味著如果與 IFace.PrintStauts 進行關(guān)聯(lián)就等于承認自己和 Base.PrintStatus 是同質(zhì)的,這樣的話,為什么不直接在 Derived2 里重寫 PrintStatus 呢?在《基類與接口混合繼承的聲明問題》中,我示范了一個實用的設(shè)計,用 new 和接口重新實現(xiàn)(Interface reimplementation)來糾正非預(yù)期的多態(tài)行為。

       

      6. 最后...

              當(dāng)我的朋友拿著問題來找我時,我通常都不會直接給出我的答案,而是盡我的能力向他提供足夠多的可用信息,以便他能夠根據(jù)他所面臨的實際情況作出處理,畢竟,我不會比他更了解他的問題,而他也應(yīng)該形成他自己的關(guān)于他的問題的思考。我希望浪子能用自己的答案回答他所提出的問題,因為只有這樣,那些知識才真正屬于他,并且我也相信本文已經(jīng)提供了足夠多的可用信息。

      posted @ 2006-03-13 09:40  Allen Lee  閱讀(13088)  評論(22)    收藏  舉報
      主站蜘蛛池模板: 亚洲午夜伦费影视在线观看| 色老头亚洲成人免费影院| 午夜高清福利在线观看| 中文字幕 日韩 人妻 无码| 国产精成人品日日拍夜夜免费| 亚洲欧洲精品一区二区| 国产色a在线观看| 国内精品视频一区二区三区八戒| 日本少妇xxx做受| 国产激情一区二区三区不卡| 四虎在线成人免费观看| 国产午夜三级一区二区三| 下面一进一出好爽视频| 日韩有码中文字幕av| 亚洲国产理论片在线播放| 亚洲无人区码一二三四区| 欧洲精品色在线观看| av大片在线无码免费| 91偷自国产一区二区三区| 日韩精品中文字幕有码| 97无码人妻福利免费公开在线视频| 国产精品综合在线免费看| 久久精品国产99国产精品严洲| 久久99精品久久久久久不卡| 欧美日韩人人模人人爽人人喊 | 人与禽交av在线播放| 久久免费网站91色网站| 少妇人妻偷人偷人精品| 亚洲一本大道在线| 国产精品无码无卡在线观看久| 免费无码又爽又刺激高潮虎虎视频| 成人精品自拍视频免费看| 亚洲成av人片色午夜乱码| 韩国午夜理伦三级| 视频一区视频二区在线视频| 日本韩国一区二区精品| 国产69精品久久久久乱码免费| 国产日韩入口一区二区| 人妻激情另类乱人伦人妻| 久久精品一本到东京热| 人妻少妇偷人无码视频|