<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>

                ??碼云GVP開源項目 12k star Uniapp+ElementUI 功能強大 支持多語言、二開方便! 廣告
                # 原則9:在你的 API 中避免轉換操作 **By D.S.Qiu** 尊重他人的勞動,**支持原創,轉載請注明[出處](/blog/1983118):[http://dsqiu.iteye.com](http://dsqiu.iteye.com)** 轉換操作引入不同類之間的替代性。替代性的意思是一個類可以被另外一個類代替。這有一個好處:一個子類可以代替基類,像下面 shape 繼承結構給出例子。你定義基類 Shape 并自定義它的子類: Rectangle , Ellipse , Circle 等。在期望是 Shape 的地方你可以用 Circle 代替它。這是使用多態的替代性。這個是因為 Circle 是 Shape 的一個特別類型。當你創建一個類,某些轉換是自動被允許的。任何對象都可以替代 .NET 類結構的基類 System.Object 的對象。相同的原理,你創建的任何類的對象都可以隱式代替它實現的接口,或者任何基類的接口,或者任何基類。語言還支持一系列的數值轉換。 當你定義類的轉換操作符,等于告訴編譯器你的類型可以代替目標類型。這些轉換經常會導致一些微妙錯誤因為你的類型可能不是能很完美替代目標類型。有一個副作用就是當你修改目標類型的狀態可能不會對你的類型產生相同的影響。更糟糕的是,如果你的轉換操作符返回一個臨時對象,這個臨時對象將會被垃圾回收而永久丟失。應用轉換操作符的規則是基于編譯時的類型,而不是運行時的對象的類型。使用你的類可能需要執行多次轉換操作符,這個實踐會導致代碼很難維護。 如果你想轉換任意類型到你的類型,使用構造器。這個更清晰的反應了創建對象的行為。轉換操作符會在代碼中引入很難發現的問題。假設你的代碼的庫的繼承結構如圖1.1一樣。 Circle 和 Ellipse 是 Shape 的子類。你打算不考慮這個結構關系,因為你認為,即使 Cirle 和 Ellipse 是相關的,但你不想要結構中非抽象的兄弟關系,當你嘗試從 Ellips 類對象得到 Circle 對象就會發現幾個實現問題。然而,你實現的每個 Circle 對象都可以是一個 Ellipse 對象。此外,一些 Ellipse 對象可以代替 Circle 對象。 ![](https://box.kancloud.cn/2016-01-31_56ad669fe76ed.jpg) 這導致你添加兩個轉換操作符。Circle 對象都是 Ellipse 對象,所以你需要添加一個隱式轉換從 Circle 創建一個 Ellipse 對象。當一個類型需要轉換到另一個類型隱式轉換都會被調用。相反,顯式轉換只有在程序員在代碼中強制轉換才會被調用。 ``` public class Circle : Shape { private PointF center; private float radius; public Circle() : this(PointF.Empty, 0) { } public Circle(PointF c, float r) { center = c; radius = r; } public override void Draw() { //... } static public implicit operator Ellipse(Circle c) { return new Ellipse(c.center, c.center,c.radius, c.radius); } } ``` 既然你已經有一個隱式轉換操作符,你可以任何期望是 Ellipse 的地方使用 Circle 。此外,這個轉換是自動發生的: ``` public static double ComputeArea(Ellipse e) { // return the area of the ellipse. return e.R1 * e.R2 * Math.PI; } // call it: Circle c1 = new Circle(new PointF(3.0f, 0), 5.0f); ComputeArea(c1); ``` 這個例子就是我說的代替:一個 Circle 對象可以代替 Ellipse 對象。ComputeArea 函數甚至能在代替后工作。你獲得好運氣。但是研究下這個函數: ``` public static void Flatten(Ellipse e) { e.R1 /= 2; e.R2 *= 2; } // call it using a circle: Circle c = new Circle(new PointF(3.0f, 0), 5.0f); Flatten(c); ``` 這就不能工作了。 Flatten 方法需要 Ellipse 的參數。編譯器會某些情況下會將 Ellipse 轉換為 Circle 。你定義的隱式轉換就是實現這個工作的。你的轉換會被調用,而且 Flatten 函數接受的被隱式轉換創建的 Ellipse 對象。這個臨時對象唄 Flatten 函數修改,并且立即變成了垃圾。副作用預期是從你的 Flatten 函數發生的,但是僅僅是一個臨時變量。最后的結果就是 Circle c 沒有發生任何事情。 將隱式轉換該為強制轉換: ``` Circle c = new Circle(new PointF(3.0f, 0), 5.0f); Flatten((Ellipse)c); ``` 原來的問題還是存在。你只是強制你的使用添加強制轉換來引起這個問題。你還是創建臨時對象, Flatten 函數作用在這個臨時對象上,并且丟失這個對象。 Circle 根本沒有被改變。相反,如果你創建一個構造器轉換 Circle 到 Ellipse ,這個操作就更清晰了: ``` Circle c = new Circle(new PointF(3.0f, 0), 5.0f); Flatten(new Ellipse(c)); ``` 大多數程序員會看到上面兩行代碼而立即發現傳給 Flatten() 的 Ellipse 的任何修改都會丟失。他們會持有一個新的對象來修復這個問題: ``` Circle c = new Circle(new PointF(3.0f, 0), 5.0f); Flatten(c); // Work with the circle. // ... // Convert to an ellipse. Ellipse e = new Ellipse(c); Flatten(e); ``` 變量持有了被 Flatten 修改的 Ellipse 對象。通過構造函數代替轉換操作符,你還沒有失去任何功能,你只是創建新對象就使它更清晰。(老練的 C++ 程序員,應注意, C# 調用構造函數不會進行隱式或顯式轉換。您可以創建只有當你明確地使用 new 運算符的新對象,并在沒有其他時候。所以在 C# 構造器中不需要 explicit 關鍵字。) 轉換操作符你使得對象返回原來沒有的行為的域。這會其他一些問題。你隱藏了一個大漏洞在封裝的類中。當強制你的類型轉換到另一對象時,使用你這個類的可以訪問內部變量。在原則26中討論了最好避免的所有原因。 轉換操作符引入了代替的一個方式但會以前你的代碼拋出問題。你必須明確:用戶預期的類可以在你創建的這個類的對象任何地方使用。當對象的代替可用,你使得調用者用的是你創建的臨時對象或能訪問內部域。這個微妙的錯誤很難被發現因為編譯器產生了轉換對象的代碼。在你的 API 里避免轉換操作。 小結: 這個原則雖然很簡短但卻很精辟,告訴我們實際編程應該更多通過規范來約束代碼的行為,而不是將諸多情況都交給編譯器來處理,這樣總會被自己坑到的,要能在代碼中嚴格控制自己的邏輯。周末還有好多工作,加油! 歡迎各種不爽,各種噴,寫這個純屬個人愛好,秉持”分享“之德! 有關本書的其他章節翻譯請[點擊查看](/category/297763),轉載請注明出處,尊重原創! 如果您對D.S.Qiu有任何建議或意見可以在文章后面評論,或者發郵件(gd.s.qiu@gmail.com)交流,您的鼓勵和支持是我前進的動力,希望能有更多更好的分享。 轉載請在**文首**注明出處:[http://dsqiu.iteye.com/blog/1983118](/blog/1983118) 更多精彩請關注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>

                              哎呀哎呀视频在线观看