作为一名进入 .NET 的 Java 开发人员,我想了解 IDisposable 接口。有人可以尝试解释一下这一点以及它与 Java 中发生的情况有何不同吗?谢谢。
我写了一系列关于 IDisposable 的详细文章。
这里的基本思想是,有时您确实需要确定性地处置资源。 IDisposable 提供了这种机制。
例如,假设您在窗口中有一个控件。当它被创建时,它会在内部创建一个窗口句柄(HWND)。当您从窗口中删除该控件并且不再使用该控件时,该控件就符合垃圾收集的条件 - 但它不会立即被收集。事实上,无法保证多长时间才会被收集。
在 GC 运行并处理孤立控件之前,它仍然会使用资源,因为它仍然持有 HWND。
IDisposable 为包含需要与 GC 分开清理的代码的对象提供了一种方法,以便由对象的用户显式清理。在控件的情况下,我们可以调用
myControl.Dispose(),
,它将立即同步清理控件使用的“本机”资源 (HWND)。
java 1.7 中新引入了 try-with-resource 语句。
try (BufferedReader br = new BufferedReader(new FileReader(path))) {
return br.readLine();
}
这里使用的对象必须实现AutoCloseable接口。它与 IDisposable 不完全相同,但
close()
在finally中自动调用。这提供了实现类似行为的机会。
上面的代码与
相同BufferedReader br = new BufferedReader(new FileReader(path));
try {
return br.readLine();
} finally {
if (br != null) br.close();
}
在 java 教程中阅读更多相关信息。示例代码来自那里。
在某些情况下,您需要可靠地处置您的班级所拥有的资源。例如,打开的连接应该在适当的时间关闭,而不是在 GC 决定收集内存时关闭。在 .NET 方法中,按照惯例使用
Dispose
来实现此目的。可以在try ... finally
块中调用,例如:
IConnection conn = OpenConnection();
try{
...
}finally{
conn.Dispose();
}
因为这种模式被广泛使用,所以有一个语法糖:
using(IConnection conn = OpenConnection()){
} // Dispose is called at the end.
由于这种语法非常简洁,有时在不拥有资源但需要在使用结束时执行一些操作的对象上实现
IDisposable
很有用。例如。考虑上课
class TimeMeasure: IDisposable{
public void Measure(string operation) { ... } // recourds operation time
public void Dispose(){
... // print timings for all operations
}
}
用途:
using(TimeMeasure m = new TimeMeasure())
{
DoFoo();
m.Measure("Foo");
DoBar();
m.Measure("Bar");
} // timings for 'Foo' and 'Bar' are printed here
在 Java 中或多或少等效的接口是
Closeable
。但没有简单的语法来确保其调用。
IDisposable
应该如何实施?这有点棘手:
Dispose
调用或 GC 来释放它们,但不能同时通过两者来释放它们。因此,您需要一个指示处置事实的标志。为了减少代码重复,处理代码被移至单独的方法中。示例:
bool disposed;
public void Dispose(){
Dispose(true);
GC.SuppressFinalize(this); // tell GC not to call Finalizer() for this object
}
~MyObject(){
Dispose(false);
}
void Dispose(bool manualDisposing){
if(!disposed){
disposed = true;
if(manualDisposing)
... // call Dispose on all IDisposable fields
... // dispose all native resources
}
}
TimeMeasure
类一样,不需要Finalizer,你只需在Dispose
中执行必要的逻辑即可。基本上,高层的 IDisposable 与
using
关键字相结合,为您提供对常见 Java 习惯用法的语法支持,如下所示:
Connection c = null;
try {
c = getConnection();
....
} finally {
if (c != null) {
c.close();
}
}
如果 Java 有一个 using 关键字和一个带有
close()
方法的 IDisposable 接口,它可能看起来像这样:
//psudo-Java
using(Connection c = getConnection()) {
.....
}
在该块的末尾隐式调用 close 方法。无需尝试/最终。
话虽如此,IDisposible 有一个相当复杂的合约,主要涉及释放非托管内存。你必须努力使用 Java 才能拥有非托管内存(基本上使用 JNI 和 Swing 组件,你就有这个概念),但它在 .NET 中更常见,因此有对该概念的语言支持。
IDisposable 接口用于手动释放非托管资源。
没有同等的。
当您使用非托管资源时使用它(在Java中所有资源区域都是托管的)。
在 .net 中,由托管资源分配的内存由 GC 自动收集(与 Java 中一样)。
您还有机会使用非托管资源,您将负责内存分配和释放。
当您不再需要资源时调用此方法。