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

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                # 原則14:減少重復的初始化邏輯 **By D.S.Qiu** **尊重他人的勞動,支持原創,轉載請注明出處:[http://dsqiu.iteye.com](http://dsqiu.iteye.com)** 寫構造函數是一個反復的工作。很多開發者總是寫了第一個構造器然后復制粘貼代碼到另外一個構造器,以滿足多個重載函數接口的定義。但愿,你不是其中的一個。如果你是,那么請停止這么做。老練的 C++ 程序員會提取出通用的算法成為一個 private 輔助方法。但是,還是請停止那樣做。如果你發現多個構造函數包含相同的邏輯,將這個邏輯提取到一個通用的構造器中。這樣做的好處是,你可以避免代碼復制,構造器初始化會產生更高效的代碼。 C# 編譯器將構造函數識別為特殊的語法,進而移除重復的變量初始化和重復基類構造函數的調用。這樣使得最后對象執行最少的代碼去進行初始化。同時你因把這個任務交個通用構造函數寫的代碼是最少的。 構造初始化允許一個構造函數調用另一個構造函數,下面的這個用法的例子: ``` public class MyClass { // collection of data private List<ImportantData> coll; // Name of the instance: private string name; public MyClass() : this(0, "") { } public MyClass(int initialCount) : this(initialCount, string.Empty) { } public MyClass(int initialCount, string name) { coll = (initialCount > 0) ? new List<ImportantData>(initialCount) : new List<ImportantData>(); this.name = name; } } ``` C# 4.0支持默認參數,你可以將構造函數的代碼進一步減少。你可以在一個構造函數中使用默認參數替換幾個或者所有的參數的值: ``` public class MyClass { // collection of data private List<ImportantData> coll; // Name of the instance: private string name; // Needed to satisfy the new() constraint. public MyClass() : this(0, string.Empty) { } public MyClass(int initialCount = 0, string name = "") { coll = (initialCount > 0) ? new List<ImportantData>(initialCount) : new List<ImportantData>(); this.name = name; } } ``` 選擇默認參數而不是多個函數重載是一個很好的權衡(查看原則10)。默認參數可以有更多選擇。上面的 MyClass 就指定了兩個參數的默認值。使用者可以指定每個參數的值。使用重載產生所有排列構造函數需要四個不同的重載構造函數:沒有參數的構造函數,需要參數為 initial 的構造函數,需要參數為 name 的構造函數,以及兩個參數都需要的構造函數。你的類的成員變量個數增加,重載構造函數的個數就以所有參數的排列數增長。這么復雜使得默認參數是一個非常有效的方法減少潛在需要重載構造函數的數量。 定義所有參數默認參數的構造函數意味著你代碼 調用 new MyClass() 是合法的。當你希望這個概念,你需要顯示創建一個無參的構造函數,正如上面例子一樣。然而大部分的有默認參數,泛型的類使用 new() 約束不會接受使用所有默認參數的構造函數。為了滿足 new() 約束,類必須有一個顯示無參構造函數。總之,你需要有一個無參的構造函數滿足泛型類或泛型方法的 new() 的約束。這也不是說所有的類都需要一個無參構造函數。然后如果你支持這個,你可以保證無參構造函數在所有情況都可以工作,即使是 new() 約束的泛型類的調用。 你該注意到第二個構造函數指定 "" 為 name 參數的默認值,而不是更常見的 string.Empty 。因為 string.Empty 不是編譯時期常量。它是 string 類定義的 static 屬性。因為他不是編譯時期的常量,你不能使用它作為參數的默認值。 然而,使用默認參數代替函數重載使得你的類和所有使用它的客戶更加耦合(查看原則10)。特別地,參數的名字和默認值變成了 public 接口的一部分。改變參數的默認值需要重新編譯客戶的代碼才能發現這些改變。這使得重載構造函數在潛在變化的未來有更多彈性。你可以添加新的構造函數,或者改變那些沒有指定值而不會破壞客戶端代碼的構造函數的默認行為。 C# 版本1到3是不支持默認參數,盡管這是這個問題更好的解決方法。你必須使得每個構造函數就像單獨的函數。隨著構造函數的增多,意味著會有更多的重復代碼。使用構造函數鏈,即讓一個構造函數調用另外一個構造函數,而不是創建通用的方法。一些低效就會在提取構造函數通用邏輯的替代方法中體現出來: ``` public class MyClass { // collection of data private List<ImportantData> coll; // Name of the instance: private string name; public MyClass() { commonConstructor(0, ""); } public MyClass(int initialCount) { commonConstructor(initialCount, ""); } public MyClass(int initialCount, string Name) { commonConstructor(initialCount, Name); } private void commonConstructor(int count, string name) { coll = (count > 0) ? new List<ImportantData>(count) : new List<ImportantData>(); this.name = name; } } ``` 這個版本看起效果一樣,但是它產生太多低效的對象代碼。編譯器會增加幾個函數代碼在你的構造函數中。它會增加對所有成員變量的進行初始化。它會調用基類的構造函數。如果你寫了自己的通用函數,編譯器不會提取重復代碼。如果你按著下面的方式寫, IL 代碼和第二個版本是一樣的: ``` public class MyClass { private List<ImportantData> coll; private string name; public MyClass() { // Instance Initializers would go here. object(); // Not legal, illustrative only. commonConstructor(0, ""); } public MyClass(int initialCount) { // Instance Initializers would go here. object(); // Not legal, illustrative only. commonConstructor(initialCount, ""); } public MyClass(int initialCount, string Name) { // Instance Initializers would go here. object(); // Not legal, illustrative only. commonConstructor(initialCount, Name); } private void commonConstructor(int count, string name) { coll = (count > 0) ? new List<ImportantData>(count) : new List<ImportantData>(); this.name = name; } } ``` 如果你用第一個版本寫構造函數,在編譯看來,你是這樣寫的: ``` // Not legal, illustrates IL generated: public class MyClass { private List<ImportantData> coll; private string name; public MyClass() { // No variable initializers here. // Call the third constructor, shown below. this(0, ""); // Not legal, illustrative only. } public MyClass(int initialCount) { // No variable initializers here. // Call the third constructor, shown below. this(initialCount, ""); } public MyClass(int initialCount, string Name) { // Instance Initializers would go here. object(); // Not legal, illustrative only. coll = (initialCount > 0) ? new List<ImportantData>(initialCount) : new List<ImportantData>(); name = Name; } } ``` 區別在于編譯器既不用重復產生基類構造函數,又不用復制實例變量初始化語法到每個構造函數中。實際上基類構造函數只會在最后一個的構造函數中才是有意義的:你不能包含多于一個構造函數的初始化。你可以使用 this() 把這個委托給另一個方法,或者使用 base() 調用基類構造函數。但你不能同時調用這兩個。 如果你還不清楚構造函數的初始化語法?你可以考慮下只讀常量。在這個例子里,對象的 name 這就是說,你應該設置為只讀。這就會導致使用通用函數構造對象產生編譯錯誤: ``` public class MyClass { // collection of data private List<ImportantData> coll; // Number for this instance private int counter; // Name of the instance: private readonly string name; public MyClass() { commonConstructor(0, string.Empty); } public MyClass(int initialCount) { commonConstructor(initialCount, string.Empty); } public MyClass(int initialCount, string Name) { commonConstructor(initialCount, Name); } private void commonConstructor(int count, string name) { coll = (count > 0) ? new List<ImportantData>(count) : new List<ImportantData>(); // ERROR changing the name outside of a constructor. this.name = name; } } ``` C++ 程序員會在每個構造函數中初始化 name ,或者在通用函數中扔掉它的常量性質。 C# 的構造函數提供了更好的選擇。但在大多數瑣碎的類都會包含不止一個構造函數。它們的職責就是初始化對象的所有成員變量。這些函數有相似或者相同的共享的邏輯是很常見的。使用 C# 構造函數初始化語法去提取它們通用的算法,以至于你可以只把它們寫一次而且執行一次。 默認參數和重載都各有優劣。一般來說,你應該選擇默認參數而不是重載構造函數。畢竟,如果你讓客戶端指定所有參數的值,你構造函數必須能夠處理所有他們指定的值。你的原始默認值應該總是顯而易見的并且不會產生異常。因此,即使改變默認參數的值是技術上的破壞,對于客戶端代碼是不會察覺到的。他們的代碼還是用原來的值,而且那些原來的值應該能產生可以接受的行為。所以要最小化使用默認參數的潛在危害。 這是最后一個關于 C# 對象初始化的原則。是時候該回顧一個類型對象構造的一系列的事件的順序了。你應該盡量讓每個成員變量在整個構造過程中只精確的初始化一次。最好的方式是你完成變量初始化越早越好。下面就是一個實例第一次構造對象的操作順序: 1.static 變量默認存儲為0。 2.static 變量初始化執行。 3.基類靜態構造函數執行。 4.靜態構造函數執行。 5.實例成員變量默認存儲為0。 6.實例成員變量初始化執行。 7.恰當的基類實例構造函數執行。 8.實例構造函數執行。 后續同一類型的對象初始化從第5步開始因為類初始化只會執行一次。而且,步驟6和7會被優化構造函數初始化語法會引起編譯器移除重復的指令。 C# 編譯器保證一個對象被創建是所有東西都被初始化好。至少,在對象創建時,所有對象內存值都被設置為0是確定的。這對靜態成員變量和實例成員變量都是正確的。你的目標就是確保你初始化所有變量的代碼只執行一次。適用初始化語法去初始化簡單的資源。適用構造函數初始化需要復雜邏輯的變量。同時提取調用其他構造函數,以最小化重復。 小結: 這個原則講的內容還是很重要的,尤其是最后的總結——對象變量初始化的過程。總之,對于復雜靜態變量邏輯(特別是會有異常拋出)使用靜態構造函數實現。 歡迎各種不爽,各種噴,寫這個純屬個人愛好,秉持”分享“之德! 有關本書的其他章節翻譯請[點擊查看](/category/297763),轉載請注明出處,尊重原創! 如果您對D.S.Qiu有任何建議或意見可以在文章后面評論,或者發郵件(gd.s.qiu@gmail.com)交流,您的鼓勵和支持是我前進的動力,希望能有更多更好的分享。 轉載請在**文首**注明出處:[http://dsqiu.iteye.com/blog/2078078](/blog/2078078) 更多精彩請關注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>

                              哎呀哎呀视频在线观看