在 C++ 程式中可使用 destructor 來釋放資源,而 C# 中則透過 GC(Garbage Collector) 來自動回收資源,GC 為 CLR 內的機制之一,凡是在 CLR 所運作中的程式都可稱為 managed code,不是在 CLR運作中的程式就稱為 unmanaged code(stream、與資料庫的連結、COM物件…等)。也就是說若是使用了 unmanaged code 後並沒有釋放資源,則會等到程式結束時才釋放。

在 C# 中使用 unmanaged code 想要釋放資源必須要使用者明確的寫出釋放資源的方法,也就是使用 Finalize() 和 Dispose()。呼叫 Finalize() 並不會馬上執行而是交給 GC 來處理,這邊不多做討論;而 Dispose() 則可是明確通知 GC 進行資源回收。可透過 using statement 以及 try/finally 來呼叫 Dispose()。

以執行 sql 程式為例:

public void ExecuteCommand(string connString, string commandString)
{
    SqlConnection myConnection = new SqlConnection(connString);
    SqlCommand mySqlCommand = new SqlCommand(commandString, myConnection);

    myConnection.Open();
    mySqlCommand.ExecuteNonQuery();
}

SqlConnection 以及 SqlCommand 的實體會一直存在記憶體直到程式結束。因此可再修改為:

public void ExecuteCommand(string connString, string commandString)
{
    SqlConnection myConnection = new SqlConnection(connString);
    SqlCommand mySqlCommand = new SqlCommand(commandString, myConnection);

    myConnection.Open();
    mySqlCommand.ExecuteNonQuery();

    mySqlCommand.Dispose();
    myConnection.Dispose();
}

還是有可能發生問題,若是在呼叫 Dispose() 之前發生 exception,則永遠不會釋放記憶體,因此可使用 using statement 來確保 Dispose() 一定會被呼叫到:

public void ExecuteCommand(string connString, string commandString)
{
    using (SqlConnection myConnection = new SqlConnection(connString))
    {
        using (SqlCommand mySqlCommand = new SqlCommand(commandString, myConnection))
        {
            myConnection.Open();
            mySqlCommand.ExecuteNonQuery();
        }
    }
}

也可用 try/finally 來改寫上述程式,其 IL 內容與使用 using statement完全一樣:

public void ExecuteCommand(string connString, string commandString)
{
    SqlConnection myConnection = null;
    SqlCommand mySqlCommand = null;
    try
    {
        myConnection = new SqlConnection(connString);
        try
        {
            mySqlCommand = new SqlCommand(commandString, myConnection);
            myConnection.Open();
            mySqlCommand.ExecuteNonQuery(); 
        }
        finally
        {
            if (mySqlCommand != null)
                mySqlCommand.Dispose();
        }
    }
    finally
    {
        if (myConnection != null)
            myConnection.Dispose();
    }
}

也就是一個 using statement 可以用一個 try/finally來改寫。


若 using statement 內的類別沒有支援 IDisposable interface,在編譯時就會產生錯誤:

using (string msg = "This is a message")
{
    Console.WriteLine(msg);
}


在編譯時期就知道有支援 IDisposable interface 的型態才可使用 using statement,在編譯時期無法確認則無法使用:

using (object obj = Factory.CreateResource())
    Console.WriteLine(obj.ToString());

但可透過as statement轉型後使用

object obj = Factory.CreateResource();
using (obj as IDisposable)
{
    Console.WriteLine(obj.ToString());
}