<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                企業??AI智能體構建引擎,智能編排和調試,一鍵部署,支持知識庫和私有化部署方案 廣告
                # 原則23:理解接口方法和虛函數的區別 **By D.S.Qiu** **尊重他人的勞動,支持原創,轉載請注明出處:[http://dsqiu.iteye.com](http://dsqiu.iteye.com)** 咋一看,實現接口和重載一個虛函數似乎是一樣的。都是定義一個在另一個類中聲明的成員。第一眼的感覺是很有欺騙性的。實現接口和重載虛函數是非常不同的。在接口聲明的成員是非虛的——至少不是默認的。子類不能重載基類實現的接口的成員。接口可以顯示實現,可以把它們中 public 接口中隱藏。它們的概念不同而且使用也不同。 但是你可以這樣實現接口以至于子類可以修改你的實現。你只需要對子類做一個 hook 就行了。 為了說明它們的不同,定義一個簡單的幾塊和它的實現類: ``` interface IMsg { void Message(); } public class MyClass : IMsg { public void Message() { Console.WriteLine("MyClass"); } } ``` Message() 方法是 MyClass 類公有接口的一部分。Message 也可以通過 IMsg 指針訪問,它是 MyClass 類型的一部分。現在通過添加子類變得更復雜: ``` public class MyDerivedClass : MyClass { public void Message() { Console.WriteLine("MyDerivedClass"); } } ``` 注意到我不得不添加 new 關鍵字用以區別之前的 Message 方法(查看原則33)(譯者注:這應該是第一版的敘述)。 MyClass.Message() 是非虛的。子類不能提供重載的 Message 版本。 MyClass 創建了新的 Message 方法,但是這個方法沒有重載 MyClass.Message : 它會被隱藏。更重要的是,MyClass.Message 仍然可通過 IMsg 引用訪問: ``` MyDerivedClass d = new MyDerivedClass(); d.Message(); // prints "MyDerivedClass". IMsg m = d as IMsg; m.Message(); // prints "MyClass" ``` 接口的方法是非虛的。當你實現接口,你就在這個類中聲明這個合約的具體的實現。 但是你經常想要創建接口,在基類實現它們,并且在子類修改它們的行為。你確實可以做到。你有兩種選擇。要是你不能接觸到基類,你可以在子類中重新實現接口: ``` public class MyDerivedClass : MyClass { public new void Message() { Console.WriteLine("MyDerivedClass"); } } ``` 新增的關鍵字使得 IMsg 改變行為子類的行為以至于 IMsge.Message 可以調用子類的版本: ``` MyDerivedClass d = new MyDerivedClass(); d.Message(); // prints "MyDerivedClass". IMsg m = d as IMsg; m.Message(); // prints " MyDerivedClass " ``` 如果你仍然使用 new 關鍵字在 MyDerivedClass.Message 方法上。給你個提示:仍然還會有問題(查看原則33)。子類的版本仍然可以通過子類的引用訪問到: ``` MyDerivedClass d = new MyDerivedClass(); d.Message(); // prints "MyDerivedClass". IMsg m = d as IMsg; m.Message(); // prints "MyDerivedClass" MyClass b = d; b.Message(); // prints "MyClass" ``` 修復這個問題方法是修改基類,聲明接口方法為 virtual : ``` public class MyClass : IMsg { public virtual void Message() { Console.WriteLine("MyClass"); } } public class MyDerivedClass : MyClass { public override void Message() { Console.WriteLine("MyDerivedClass"); } } ``` MyDerivedClass——其他所有繼承自 MyClass ——都可以聲明它們自己的 Message() 方法。重載的版本總是會被調用:無論是通過 MyDerivedClass 引用,還是通過 IMsg 引用,或者是通過 MyClass 引用。 要是你不喜歡虛函數的摻雜,你只需要在定義 MyClass 上定義做一個小的變化: ``` public abstract class MyClass : IMsg { public abstract void Message(); } ``` 是的,你可以實現接口卻沒有實際實現這個接口的方法。通過聲明接口方法的 abstract 版本,你就是聲明繼承的子類都必須實現這個接口。 IMsg 是 MyClass 聲明的一部分,但是定義的方法被延遲到子類中實現。 子類可以防止進一步的重載的密封方法: ``` public class MyDerivedClass2 : MyClass { public sealed override void Message() { Console.WriteLine("MyDerivedClass"); } } ``` 另一個解決方案是實現這個的接口中調用一個虛方法,以讓子類加入接口的合約中。你可以在 MyClass 中這樣做: ``` public class MyClass2 : IMsg { protected virtual void OnMessage() { } public void Message() { OnMessage(); Console.WriteLine("MyClass"); } } ``` 任何子類重載 OnMessage() 添加它們自己的工作到聲明在 MyClass2 的 Message() 方法中。這個模式你在前面類實現 IDisposable 中見過 (查看原則17)。 顯式接口實現(查看原則31)使你能夠實現 一個接口,也可以隱藏你的類的公共接口。它的使用實現接口和重載虛函數變得不那么清晰。你可以使用顯示接口實現限制使用者可以有訪問更多的接口方法版本。 IComarable 習慣會在原則31詳細展示這點。 還有最后一個添加接口和基類一起工作的驚喜。基類可以提供接口中方法的默認實現。然后,子類可以聲明實現這個接口并從基類中繼承這個接口的實現,正如下面例子一樣。 ``` public class DefaultMessageGenerator { public void Message() { Console.WriteLine("This is a default message"); } } public class AnotherMessageGenerator :DefaultMessageGenerator, IMsg { // No explicit Message() method needed. } ``` 注意到子類可以聲明接口是其的一部分合約,即使它沒有提供任何 IMsg 方法的實現。只要它由恰當的公有可訪問簽名的方法,那么滿足接口的合約。使用這個方法,你可以不用顯示接口實現。 實現接口比創建和重載虛函數有更多選擇。你可以創建 sealed 實現,虛實現,或者是類繼承接口的抽象約束。你也可以創建 sealed 實現并提供一個虛函數調用來實現接口。你可以準確地決定怎樣和什么時候子類修改你的基類實現的接口的默認行為。接口方法不是虛方法而是獨立的合約。 小結: 這個原則沒有大量枚舉接口和繼承各種組合使用的不同,那都是專牛角尖的人才去干的,而是用原理上梳理了下兩者的不同,當然也有點坑需要記住的:接口的顯示實現會隱藏子類的實現,添加 new 關鍵字可以解決這個問題,但是還不是多態。 作為接口使用,如果基類沒有 virtual 和 子類也沒有 new 那么基類實現優先級會更高!!! 歡迎各種不爽,各種噴,寫這個純屬個人愛好,秉持”分享“之德! 有關本書的其他章節翻譯請[點擊查看](/category/297763),轉載請注明出處,尊重原創! 如果您對D.S.Qiu有任何建議或意見可以在文章后面評論,或者發郵件(gd.s.qiu@gmail.com)交流,您的鼓勵和支持是我前進的動力,希望能有更多更好的分享。 轉載請在**文首**注明出處:[http://dsqiu.iteye.com/blog/2083428](/blog/2083428) 更多精彩請關注D.S.Qiu的博客和微博(ID:靜水逐風)
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看