我们知道在代码里自己写 Dispose 来释放一些资源。但是你真的用对了吗 ? 本篇介绍一些不容易注意到的 GC 相关知识 。

到底什么时候需要使用到 IDispose ?

当前类中出现 IO 操作。或者其他 跨语言调用,窗口和网络连接 之的非托管资源调用,这时才需要自己是实现一个IDispose 接口。其他的时候你并不需要去实现这样一个接口。我的做法是一般的类继承一个 IReset 接口,这个接口内只包含一个 Reset 函数 .
Dispose 接口是一个显示的调用,如果我们没有写这个接口,运行时他会在执行析构函数的时候清理资源。

了解析构函数(终结器)

在使用Dispose 接口的同时 你或许同时会用到一个终结器。打个比方 如果你忘记显示调用 Dispose 函数的时候,程序还是会帮你清除非托管资源。

先观察以下析构函数

1
2
3
4
5
6
7
class Car 
{
~Car() // finalizer
{
// cleanup statements...
}
}

上面的代码很简单,他等价于下面的代码

1
2
3
4
5
6
7
8
9
10
11
protected override void Finalize()  
{
try
{
// Cleanup statements...
}
finally
{
base.Finalize();
}
}

现在你应该已经观察到这一行代码被隐式调用了,这行代码的意义是将当前运行时 Finalize 队列中创建一个条目,等待回收器来处理该队列。

1
base.Finalize();  

正确的 “Dispose”

我们再观察下面的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class MyClass
{
// 这里实现了IDispose中的 Dispose方法
public void Dispose()
{
//执行资源释放 xxxxxxxx

//告诉GC此对象的Finalize方法不再需要调用
GC.SuppressFinalize(this);
}

~MyClass()
{
Dispose();
}
}

//下面我们使用using使用这个类
using(MyClass s = new MyClass()){}

上面我们使用了 GC.SuppressFinalize(this); 函数,他会告诉GC的Finalize队列中移除当前,不必在执行 Finalize()

注意事项

  1. 在非托管资源操作中才去重写dispose接口,否则使用其他自定义接口去实现 Reset 操作
  2. 在非托管资源操作中才去重写析构函数,否则空析构函数也会造成不必要的性能损失
  3. 重写dispose接口的同时别忘了重写析构函数

文外话

在 C# 语言里对析构函数与终结器的描述似乎是同一个东西,这让我有一点困惑。我查询了一些论文他们是这样说的。如果你感兴趣也可以看一下

In the C# world the terms “destructor” and “finalizer” seem to be used pretty much interchangeably, which I suspect is because the C# specification describes the non-deterministic cleanup functionality using the word “destructor”, whereas the CLR documentation always uses the word “finalizer”, so within the realms of C# they mean the same thing.

However, in the C++/CLI specification there is a distinction made between the two. It allows both deterministic and non-deterministic cleanup, and uses the term “destructor” for the deterministic functionality and “finalizer” for the non-deterministic functionality:

在C#世界术语“析构函数”和“终结”似乎要使用pretty多互换,我怀疑是因为C#规范用字“析构函数”,描述了非确定性的清理功能,而CLR的文档始终使用单词“终结”,所以C#的领域内,他们的意思是一样的。

然而,在C / CLI规范有两者之间作出区分。它同时允许确定性和非确定性的清理,并使用该确定的功能的非确定性的功能的术语“析构”和“终结”:

Microsoft 编程指南

析构函数和终结器的区别?(The difference between a destructor and a finalizer?