资源类型
托管资源:由CLR建立和释放
非托管资源:资源的建立和释放不经CLR管理。好比IO、网络链接、数据库链接等等。须要开发人员手动释放。
如何释放非托管资源
方法1.声明一个析构函数,作为类的成员函数
C#编译器在编译析构函数时,会隐式把析构函数的代码编译为等价于重写Finalize()方法的代码,从而确保执行父类的Finalize方法。
protected override void Finalize()
{
try
{
// Cleanup statements...
}
finally
{
base.Finalize();
}
}
优点:
资源管理: 提供了一种机制,确保在对象生命周期结束时进行资源清理。
自动执行: 由垃圾回收器自动调用,无需程序员手动介入,减少出错的可能性。
缺点:
- 无法确定c#对象的析构函数何时执行。
- C#析构函数的实现会延迟对象最终从内存中删除的时间。
结论:尽量避免使用析构函数。C#中通常更推荐使用IDisposable接口和Dispose方法来进行资源管理。
方法2.在类中实现system.IDispose
微软提供了IDispose接口,其中只声明了一个void的Dispose()虚函数方法。并且还为实现了IDispose接口的类提供了using释放资源的语法糖。
在实际使用上,使用实现IDispose接口和析构函数的双重实现机制。
1.继承IDispose接口的类,其析构函数调用了Dispose(false)方法,所以会释放资源。但是回收的时间不定、顺序不定、线程不定。因此析构函数中调用Dispose只是一个保险措施,并不能代替手动释放资源。
2.在自定义的类,并且继承IDisposable接口的时候,重写虚函数Dispose,
并且新建带参数的Dispose函数,在该函数实现释放资源。
public class Foo: IDisposable
{
bool m_disposed = false;//表示对象是否已经被清理
public void Dispose()
{
Dispose(true );
GC.SuppressFinalize(this);//意味着垃圾收集器认为这个对象根本就没有析构函数
}
protected virtual void Dispose(bool disposing)
{
if (!m_disposed)
{
if (disposing)
{
// //释放一些继承了IDisposable 的托管资源。一般很少
}
//在这里释放释放托管资源
m_disposed = true;
}
}
//析构函数,正常情况下调用了Dispose(),就不需要再执行析构函数。
~Foo()
{
Dispose(false);
}
注意:正常情况下,需用户手动显示调用Dispose()方法。或者使用using来释放资源,因为会自动调用Dispose()。
如果忘了using或显式调用Dispose(),那么析构函数就是兜底的了。
延伸:using 语句
使用using 语句,在一定的范围内有效,除了这个范围时,自动调用IDisposable的Dispose函数释放掉资源。
只有实现了IDisposable接口的类才可以使用。提供了机制保证Dispose方法被调用,无论using语句块顺利执行结束,还是抛出了一个异常。
using (var reader = new StringReader(manyLines))
{
string? item;
do
{
item = reader.ReadLine();
Console.WriteLine(item);
} while (item != null);
}
除了using可以达到这个目的意外,try finally也是可以的。如
var reader = new StringReader(manyLines);
try
{
string? item;
do
{
item = reader.ReadLine();
Console.WriteLine(item);
} while (item != null);
}
finally
{
reader?.Dispose();
}
}