手动实现IDisposable接口完成资源回收
之前一直想当然地认为一个对象实现了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这种写法自动回收资源:
sample.Dispose();//对象回收
using (Sample4GC sample2 = new Sample4GC())
{
// code to do
}