《Effective C#》讀書筆記——條目16:避免建立非必要的對象

來源:互聯網
上載者:User

  我們知道:C#是一門虛擬機器語言,C#編譯器首先將C#代碼編譯成IL代碼,運行程式時CLR(Common Language Runtime,通用語言執行平台)通過調用JIT(just-in-time Compiler,即時編譯器)來將IL代動態即時編譯成可執行檔機器碼。GC(Garbage Collector,垃圾收集器)自動為我們的應用程式進行記憶體管理的分配和釋放,(具體參見:瞭解.NET 記憶體管理機制),以一種高效的方式來移除記憶體中的垃圾對象,不過不管有多高效,分配和銷毀在堆上的對象總會花費掉時間。

  如果我們在一個方法中建立了過多的引用對象,會對應用程式的效能產生嚴重的影響。因此我們應該遵守下面的一些規則,可以盡量的降低GC的工作量。

 

閱讀目錄:

      1.將常用的局部變數提升為成員變數

      2.為常用的類型執行個體提供靜態對象

      3.為不可變類型提供可變的建立對象

      小節

 

1.將常用的局部變數提升為成員變數

   所有的參考型別,包括那些局部變數,都會分配到堆上。在函數退出後,函數內的所有局部變數都會立即變成垃圾對象。所以我們可以得出結論:

若是某個參考型別(實值型別無所謂)的局部變數用於被頻繁調用的常式中,那麼應該將其提升為成員變數。這既有助於減輕GC的負擔,也可以提升程式啟動並執行效率。

 在GUI編程中一個常見的錯誤就是:在表單的Paint處理函數中建立GDI(Graphics Device Interface,圖形裝置介面)對象,如下:

1         protected override void OnPaint(PaintEventArgs e)2         {3             using (Font myFont = new Font("Arial", 10.0f))4             {5                 e.Graphics.DrawString(DateTime.Now.ToString(), myFont, Brushes.Black, new Point(0, 0));6             }7             base.OnPaint(e);8         }

 

OnPaint()將會被非常頻繁的調用,每次調用都會建立一個Font對象,而包含的內容完全和上一次一樣。所以GC需要每次都為你清掃這些垃圾,嚴重影響了應用程式的效率。其實我們完全可以將Font對象提升為成員變數,是每次表單重繪時能夠重用該Font對象:

1         private readonly Font myFont = new Font("Arial", 10.0f);2 3         protected override void OnPaint(PaintEventArgs e)4         {5                 e.Graphics.DrawString(DateTime.Now.ToString(), myFont, Brushes.Black, new Point(0, 0));6 7                 base.OnPaint(e);8         }

 將常用的局部變數提升為成員變數之後,程式無需每次重繪時產生垃圾對象,減輕了GC的負擔,也提升了程式的效率。不過這裡有一個小小的限制:將實現了IDisposable介面的局部變數提升為成員變數,那麼這個成員變數依附類本身也需要實現IDisposable介面。

 

2.為常用的類型執行個體提供靜態對象

  靜態成員變數可以讓參考型別在類的各個執行個體中共用。我們可以通過提供了一個類,存放某個類型常用的執行個體的單例對象,這樣可以避免建立重複的對象。.NET Framework 的類庫中就有很多這樣的做法:Brushes類包含了一系列的靜態Brush對象,每個都包含了一種常用的顏色。它們的簡要實現如下:

 1         private static Brush blackBrush; 2         public static Brush Black 3         { 4             get 5             { 6                 if (blackBrush == null) 7                     blackBrush = new SolidBrush(Color.Black); 8                 return blackBrush; 9             }10         }

在第一次請求黑色畫刷時,Brushes將建立一個執行個體,隨意Brushes類將保留該執行個體的引用,並在後續的請求時直接返回同一個控制代碼。也就是說我們只建立了一個黑色畫刷,然後一直重用這個對象。至於其他的例如:紅色的畫刷,如果應用程式沒有使用這個資源那麼該對象也不會被分配。.NET 提供的這種方式能夠在滿足需求的前提下儘可能的少建立對象,我們在自己的應用程式中也應該這樣做。

 

3.為不可變類型提供可變的建立對象

  System.String類型時一個不可變類型:即在構造一個字串對象後,其內容不能被修改。如果對一個字串進行修改時,實際上時建立了一個新的字串對象,從前的字串對象也就變成了垃圾。看下面的代碼:

1             //How are you?2             string msg = "How";3             msg += " are";4             msg += " you";5             msg += "?";

 

string類型的+=操作符會建立一個新的字串對象並返回,對於這類拼接字串的工作應該交給更適合的string.Format()方法:

1 string msg = string.Format("{0} {1} {2}{3}", "How", "are", "you", "?");

 

如果需要進行一些比簡單拼接字串更加複雜的工作,可以考慮使用StringBuilder類,該類是一個可變的字串,用來建立不可變的string對象。對於經常變化的stirng對象,使用StringBuilder對象來替換是一個非常好的選擇 —— 當我們的某個設計需要不可變類型時,應該考慮提供一個建立對象,專門負責分步地構造出最終對象(例如:string對象之於StringBuilder對象)。這樣既可讓使用者分步地建立出最終對象,也可以保證對象的不可變性

 

小節

  GC可以高效的管理應用程式使用的記憶體,不過建立和銷毀堆上的對象仍舊需要時間,因此,應該避免建立過多的對象,不要建立那些非必要的對象,也不要在局部方法中建立太多的引用對象,可以考慮將常用的局部變數提升為成員變數,或者為最常用的類型執行個體提供靜態對象,此外還可以考慮為不可變類型提供可變的建立對象。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.