【設計原則和建議】 構造和析構對象
良好的構造和析構對象,控制對象生命周期可以較大的提高程序的性能,降低GC的壓力,減少BUG幾率。
本文還是比較簡單的,主要還是經驗的總結,很多東西也許各位已經知道,也許不知道。希望大家一起討論。
1.如果可能,避免靜態構造函數 (也成為類型構造函數)
- 性能原因 (不過因為一個類的靜態構造函數只會執行一次,這不是一個大問題)
- 靜態構造函數不應該拋出異常
2.如果可以,構造函數應該盡可能輕量級
- 職責上說,構造函數只應該構造出一個對象,而不是執行一大堆初始化等的操作
- 如果有很重量級的代碼,用靜態方法Create出來 例如WebRequest.Create
3.一個常識,調用構造函數時,會先調用父類的構造函數
4.一個類必須輸入的參數請放在構造函數的輸入參數中(或Create方法)
- 主要是邏輯和保障執行順序的原因
- 不要構造完了以后一個個屬性去賦值
5.如果要按照順序初始化私有成員,請在構造函數中進行 (或Create方法)
- 雖然目前CLR是從上至下的初始化私有成員,但是不保證將來版本的初始化順序也是一樣的
- 某些第三方工具自動格式化代碼的時候會改變
6.構造函數中不應該調用虛方法
7.使用this()和base()可以方便構造函數之間的相互調用
8.不要在不必要的情況下構造對象 (雖然有可能被優化掉)
DataSet ds = new DataSet();
ds = SqlHelper.ExecuteDataSet("select * from table1");
9.在更考慮性能的情況下,縮短對象生命周期 (大部分程序都不會這么考慮性能)
- 盡量遲的初始化
- 盡量早的銷毀對象
- 盡量小的作用域
- 如果對象有可能被復用,研究一下GC的對象復活
- 一個小提示
-
SingletonClass item1 = null;
for (int i = 0; i < 100; i++)
{
SingletonClass item2 = null;//在這兩個地方聲明,沒有區別,被優化掉了
}
10.如果對象實現了IDisposable
- 請使用using 或者直接調用Dispose
- 有些系統對象實現了Close, 其實Close內部就是調用了Dispose()
11.如果想實現一個IDisposable
簡單的實現
class ConstructorDemo : IDisposable
{
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this); //告訴CLR不要調用Finalize 這是一個非常消耗性能的方法
}
private void Dispose(bool disposing)
{
if (!disposing)
{
//清理托管資源
}
//清理非托管資源
}
}
完整的實現 http://msdn.microsoft.com/en-us/library/b1yfkh5e.aspx
12.如果想實現Singleton
View Code
public class ConstructorDemo1
{
private static SingletonClass singleton = new SingletonClass();
//簡單的類 我最喜歡用這種方式了,不夠構造函數出問題了就很討厭了
//只初始化一次 不用太關心性能問題
//如果SingletonClass構造函數拋出異常 那就完蛋了
private ConstructorDemo1()
{
}
public SingletonClass Create()
{
return singleton;
}
}
public class ConstructorDemo2
{
private static SingletonClass singleton = null;
private static object asyncLock = new object();
private ConstructorDemo2()
{
}
public SingletonClass Create()//只初始化一次 不用太關心性能問題,初始化有問題可以在這里處理啊
{
if (singleton == null)
{
lock (asyncLock)
{
if (singleton == null)
{
singleton = new SingletonClass();
}
}
}
return singleton;
}
}
public class ConstructorDemo3
{
private static Lazy<SingletonClass> singleton = new Lazy<SingletonClass>();
private ConstructorDemo3()
{
}
public SingletonClass Create()
{
return singleton.Value;//4.0 還是這樣簡單
}
}
13.了解GC的大致原理將有利于設計出符合預期的程序
- 建議參考CLR via C#
- 如果不想被GC的最簡單方式把對象放在一個static 成員上
- 一個例子關于資源被意外回收
using System;
using System.Threading;
public static class Program
{
public static void Main()
{
//注意要在Release模式下跑
Timer t = new Timer(TimerCallback, null, 0, 2000);
Console.ReadLine();
}
private static void TimerCallback(Object o)
{
Console.WriteLine("In TimerCallback: " + DateTime.Now);
GC.Collect(); //觸發GC
}
}
14.一般不使用析構函數釋放資源
- 微軟推薦用析構函數釋放非托管資源 (例如文件,網絡連接)
- 我個人建議是最好都別用析構,而是顯式回收資源(IDisposable),因為析構內部是調用Finalize ,而這東西的運行時間是不確定的。。。而且還有性能問題
View Code
public class Deconstructor
{
//非常不推薦使用Finalize,主要問題就是其調用時間是不確定的,并且有性能問題
~Deconstructor()
{
// 清理代碼 //將被轉換為下面的代碼
}
//protected override void Finalize()
//{
// try
// {
//// 清理代碼
// }
// finally
// {
// base.Finalize();
// }
//}
}
15.不要創建大量的小對象
- 例如string
- 例如WCF,webservice中序列化反序列化
PS: GC.SuppressFinalize 經常在Dispose被調用, 用于通知CLR:我自己會搞定,你不要調用Finalize
PS: GC有workstation和server模式
PS: 有GC也是會存在內存泄漏的,一般常見用有些對象一直被引用 所以無法被GC
PS: GC超時也會拋出OutOfMemoryException, 不一定是真的沒有內存了
PS:使用DotNetTrace 之類的工具分析內存中都存在什么對象,也可以分析構造和析構函數的性能
PS:使用WinDBG+SOS分析內存也很不錯(特別是生產環境)
PS:我個人喜歡靜態方法勝于實例方法(不喜歡每次構造對象),除非是設計需要,或者邏輯上應該是實例方法
部分資料來源于網絡,因為本人水平有錢,如有謬誤還請指正。

浙公網安備 33010602011771號