之前nc樓豬一直想當然地認爲一個對象實現了IDisposable接口,執行GC.Collect方法後,GC會幫助我們自動實現對所有資源的回收。比如下面的一段代碼:
1、一個繼承自IDisposable接口的類
using System;
using System.IO;
class Sample4GC : IDisposable
{
private string filePath = string.Empty;
private FileStream fs;
public Sample4GC()
{
filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "test.txt");
fs = File.Open(filePath, FileMode.OpenOrCreate);
//using (fs = File.Open(filePath, FileMode.OpenOrCreate)) //沒有這麼寫是因爲樓豬以爲GC強制回收就可以了
//{
Console.WriteLine("打開文本了...");
//}
}
/// <summary>
/// Finalize 析構函數
/// </summary>
~Sample4GC()
{
Console.WriteLine("對象被銷燬...");
}
public void Dispose()
{
GC.Collect();//強制回收
}
}
2、測試代碼
static void Main(string[] args)
{
using (Sample4GC sample1 = new Sample4GC())
{
// code to do
}
//拋出IOException,提示test.txt正由另一進程使用
using (Sample4GC sample2 = new Sample4GC())
{
// code to do
}
Console.ReadKey();
}
如您所看到的那樣,nc樓豬自以爲通過using這種寫法,就可以高枕無憂地多次實例化對象了。可是,在sample2實例化執行到構造函數的下面這行:
fs = File.Open(filePath, FileMode.OpenOrCreate);
竟然拋出了文件被另一個進程使用無法訪問的異常。
非常奇怪,難道using了之後,sample1還在佔用測試文本資源而沒有被回收?可是樓豬在Dispose方法上加了斷點,GC.Collect()方法明顯執行了一次啊,強制回收了怎麼文本資源還被另一個進程使用呢?
無奈,稍微改了一下構造函數裏的代碼,如下:
public Sample4GC()
{
filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "test.txt");
//fs = File.Open(filePath, FileMode.OpenOrCreate);
using (fs = File.Open(filePath, FileMode.OpenOrCreate)) //這樣就安全了
{
Console.WriteLine("打開文本了...");
}
}
果然這次就沒有異常了。可是像上面那樣寫還要繼承IDisposable接口幹什麼呢? 嗯...難道是Dispose方法裏的GC.Collect這玩意沒有回收文件流對象麼?
好吧,那就把 using (fs = File.Open(filePath, FileMode.OpenOrCreate)) 這種寫法再改回原來的 fs = File.Open(filePath, FileMode.OpenOrCreate) 這種不安全的寫法,然後再改進Dispose方法試試看:
public void Dispose()
{
if (fs != null)
{
//fs.Close();//關閉
fs.Dispose();//文件流回收 不管Close還是Dispose 都實現了回收
}
GC.Collect();//強制回收
}
我kao,果然正常,而且不管是執行文件流的Close還是Dispose方法,都實現了資源的回收,樓豬不禁一陣激動。
到這裏,樓豬可以確定,果然是樓豬自己誤解了GC.Collect方法,沒有正確實現好Dispose方法。最後模仿msdn優雅的寫法,改進一下實現代碼:
class Sample4GC : IDisposable
{
private bool isDisposed = false;
private string filePath = string.Empty;
private FileStream fs;
public Sample4GC()
{
filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "test.txt");
fs = File.Open(filePath, FileMode.OpenOrCreate);
//using (fs = File.Open(filePath, FileMode.OpenOrCreate))
//{
Console.WriteLine("打開文本了...");
//}
}
/// <summary>
/// Finalize 析構函數
/// </summary>
~Sample4GC()
{
Console.WriteLine("對象被銷燬...");
Dispose(false);
}
public void Dispose()
{
Console.WriteLine("手動銷燬對象...");
GC.SuppressFinalize(this); //通知GC:對象已經被銷燬過,不用GC處理了
Dispose(true);
}
/// <summary>
/// 自己實現的對象回收主方法
/// </summary>
/// <param name="isDisposing"></param>
private void Dispose(bool isDisposing)
{
if (!isDisposed)
{
if (isDisposing)
{
if (fs != null)//手動銷燬FileStream對象
{
//fs.Close();
fs.Dispose();
}
}
}
isDisposed = true;
}
}
在調用的時候,我們可以顯式調用Dispose方法或者通過using這種寫法自動回收資源:
Sample4GC sample = new Sample4GC();
sample.Dispose();//對象回收
using (Sample4GC sample2 = new Sample4GC())
{
// code to do
}