托管代码的主要优点之一是内置内存管理。您不需要跟踪指针、缓冲区大小、释放您完成的内存等,托管方面会为您完成这些工作。
那么为什么我们有一个 IDisposable
接口(interface)呢? MSDN says该接口(interface)用于处理非托管资源,如窗口句柄、文件等。但是为什么要求我显式调用 Dispose
方法(或使用 Using
)?
- 为什么 CLR 无法跟踪对象超出范围并自动调用
Dispose
?
Public Function DoSomething() As String
Dim reader As New StreamReader("myfile.txt")
Dim txtFromFile As String = reader.ReadToEnd()
Return txtFromFile '<==== reader goes out of scope after this line, so call Dispose automatically
End Function
- 至少,为什么垃圾收集器最终不会到达它并调用
Dispose
?
我错过了什么?
编辑
一些人(这里和其他建议使用
的答案)提出垃圾收集不够好,因为GC最终才开始收集IDisposable
。我不明白为什么该参数区分 IDisposable
和 .NET 中的任何其他对象。在您说 IDisposable
对象占用更多资源之前,请考虑:
- MSDN从上面说
IDisposable
适用于非托管对象,无论其资源要求如何 - 我见过一些非常消耗资源的 .NET 对象(System.Web.UI.Page 或 System.Data.Objects.ObjectContext 怎么样)。
最佳答案
正如我在评论中所说,CLR 不会跟踪对象何时超出范围。
让我们举个例子:
Public Function DoSomething() As String
Dim reader As New StreamReader("myfile.txt")
Dim txtFromFile As String = reader.ReadToEnd()
Return txtFromFile '<==== reader goes out of scope after this line, so call Dispose automatically
End Function
实际上需要的是它分析方法的整个主体,检查您是否已将此读取器的引用传递给另一个方法,或者是否将引用存储在字段中。
然后需要确定其他方法是否已将引用存储在某处,或者调用其他方法等。
如果引用存储在其他地方,那么那么必须推断,其目的是否是其他东西稍后会使用该引用并期望找到一个非-在那里处置实例。
将其与您的知识进行比较。您知道(希望)您是否在其他地方存储了引用,或者将该引用传递给“取得了该一次性文件的所有权”的其他内容。如果您知道这两种情况都没有发生,则可以通过添加 Using
block 将此信息传达给编译器。
Or in any event, won't the garbage collector eventually get to it and call
Dispose
?
如果a)该对象直接“包含”非托管资源,并且b)实现该对象的人都遵循最佳实践,那么他们应该在此对象上实现终结器,该终结器将对非托管资源执行清理(通常通过调用终结器和 Dispose 之间共享的方法)。
但是,您不知道下一次垃圾回收何时发生。与此同时,您可能会拒绝其他程序甚至您自己程序的另一部分访问相同的非托管资源。您应该将非托管资源视为稀缺资源。如果有人实现了 Dispose
,他们希望您在知道不再需要访问该资源时调用它(显式调用或通过 Using
)。
关于.net - 在托管环境中为什么我们需要 IDisposable,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13488112/