雖然 GC 可以自動回收資源,但若在程式中產生過多的 reference type 實體(占用heap空間),配置以及銷毀這些物件還是需要時間來處理。因此必須有效的創建這些實體,避免非必要的產生。
以OnPaint()為例:
protected override void OnPaint(PaintEventArgs e)
{
// Bad. Created the same font every paint event.
using (Font myFont = new Font("Arial", 10.0f))
{
e.Graphics.DrawString(DateTime.Now.ToString(),
myFont, Brushes.Black, new PointF(0, 0));
}
base.OnPaint(e);
}
OnPaint() 會頻繁的被呼叫使用,每一次呼叫都會創建一個 Font 實體,結束時會通知 GC 進行資源回收,此種寫法很明顯沒有效率。可以將創建 Font 的動作改放到 member variable:
private readonly Font myFont = new Font("Arial", 10.0f);
protected override void OnPaint(PaintEventArgs e)
{
e.Graphics.DrawString(DateTime.Now.ToString(),
myFont, Brushes.Black, new PointF(0, 0));
base.OnPaint(e);
}
這樣被呼叫時不會一直創建新的 Font 實體,但須注意的是,搬到 member variable 的 class 若有繼承 IDisposable 記得必須實作。也並非要把所有的區域變數都改成 member variable,是要頻繁調用的 function 這樣才有改才有意義
以上述為例,想使用一個黑色筆刷在專案內的多個視窗中使用,雖然已經抽到 member variable,但在各個視窗類別中還是有許多相同的黑色筆刷物件,這時可以使用 singleton 來確保只會創建一份實體:
private static Brush blackBrush;
public static Brush Black
{
get
{
if (blackBrush == null)
blackBrush = new SolidBrush(Color.Black);
return blackBrush;
}
}
除了上述抽到 member variable 以及 singleton 外,還需盡量減少使用不可更改的類別(代表每次 assignment 都會產生新的實體),System.String 就是不可更改的類別
string msg = "Hello, ";
msg += thisUser.Name;
msg += ". Today is ";
msg += System.DateTime.Now.ToString();
看起來沒什麼問題,其實效率非常低,等同於以下寫法:
string msg = "Hello, ";
// Not legal, for illustration only:
string tmp1 = new String(msg + thisUser.Name);
msg = tmp1; // "Hello " is garbage.
string tmp2 = new String(msg + ". Today is ");
msg = tmp2; // "Hello <user>" is garbage.
string tmp3 = new String(msg + DateTime.Now.ToString());
msg = tmp3; // "Hello <user>. Today is " is garbage.
因不可更改所以每次的 += 就會創建一個新的 string 實體返回,tmp1、tmp2、tmp3 和最一開始的 msg 這 4 個字串都變成垃圾等待回收。應改寫成以下:
string msg = string.Format("Hello, {0}. Today is {1}", thisUser.Name, DateTime.Now.ToString());
若較複雜的字串處理,建議可以使用 StringBuilder,StringBuilder 為可變字串型態,在產生最終的 string 之前,可以對其內容進行修改而不會在過程中創建新的實體出來:
StringBuilder msg = new StringBuilder("Hello, ");
msg.Append(thisUser.Name);
msg.Append(". Today is ");
msg.Append(DateTime.Now.ToString());
string finalMsg = msg.ToString();