2013年3月28日 星期四

[C#] Garbage Collection (2)

C# : Garbage Collection (2)

上篇文章
有提到Dispose,拜讀幾篇前輩的文章後,發現Dispose也是有學問的,
第一件事當然是要記得實作Dispose方法,第二件事情是要讓Dispose方法可以被重複引用。

Example
建立一個CSampleClass類別如下

class CSampleClass : IDisposable
{
        public String Name { get; set; }
        public Dictionary<String, String> ownedCar;
       
        /// <summary>
        /// 建構
        /// </summary>
        public CSampleClass()
        {
            ownedCar = new Dictionary<string, string>();
        }

        /// <summary>
        /// 解構
        /// </summary>
        ~CSampleClass()
        {
            DoDispseStep();
        }
        /// <summary>
        /// Dispose
        /// </summary>
        public void Dispose()
        {
            DoDispseStep();
        }
        /// <summary>
        /// Dispose 的步驟
        /// </summary>
        private void DoDispseStep()
        {
            Name = "";
             Console.WriteLine("移除 ownedCar {0} 筆資料", ownedCar.Count);
             ownedCar = null;
        }
}



主程式

using (CSampleClass sample = new CSampleClass())
{
     sample.Name = "JB";
     sample.ownedCar.Add("FOCUS 1.6", "2012" );
     sample.ownedCar.Add("MAZDA 1.6", "2005" );

var cars = from x in sample.ownedCar.AsQueryable()
select new { Name = x.Key , Year = x.Value };
foreach(var item in cars){
Console.Write("車種 {0},年份 {1}\n", item.Name, item.Year);
}
}



看起來不但實作了Dispose方法,連解構方法我們都加入了。
眼尖的朋友應該已經看到問題了,在DoDispseStep()方法中,這一行程式碼會有問題
Console.WriteLine("
移除 ownedCar {0} 筆資料", ownedCar.Count);

實際執行程式後,USING此物件結束後會去Call Dispose()方法,此時Dictionary物件ownedCar仍有值,所以這一行還不會有問題。 但是關閉程式時,GC機制會再去Call物件的Finalizer(解構方法),這時候ownedCar時候已經沒有參考任何值了,所以就會Error

所以要做到「讓Dispose方法可以被重複引用」,可以

ü   判斷物件是否需要Dispose

例如上面的DoDispseStep程式碼可改為

if (ownedCar != null && ownedCar.Count > 0)
{

   Console.WriteLine("
移除 ownedCar {0} 筆資料", ownedCar.Count);
   ownedCar = null;
}




ü   設定一個Dispose Flag判斷是否已經做過Dispose

在類別裡面放一個是否做過Dispose的註記:
private bool DisposedFlag = false;



DoDispseStep
程式碼改為先判斷此註記
if (DisposedFlag == false)
{

   Name = "";
   Console.WriteLine("
移除 ownedCar {0} 筆資料", ownedCar.Count);
   ownedCar = null;
   DisposedFlag = true;
}


ü   Dispose後加上 GC.SupperessFinalize(物件),告訴CLR此物件已正確清除,不必再到Finalizer(解構)

public void Dispose()
{
DoDispseStep();
GC.SuppressFinalize(this);
}


關於GC.SupperessFinalize的用法時機可參考此文 :




沒有留言:

張貼留言