清洗IDisposable资源

时间:2013-12-04 12:36:18

标签: c# garbage-collection idisposable

现在我明白当我完成一个实现IDisposable的资源时,我应该调用Dispose()方法来清理资源。

我应该在多大程度上做到这一点。

我的例子是:

我在代码中创建NotifyIcon,所以我做了类似的事情:

var icon = new Icon(...);
var contextMenu = new ContextMenu(...);
var contextMenuButton1 = new MenuItem(...);
var contextMenuButton2 = new MenuItem(...);
var contextMenuButton3 = new MenuItem(...);
// ...
var notifyIcon = new NotifyIcon(...);

所有这些都有Dispose()方法。通常我只会继续引用notifyIcon而只是处理它。

但是,它不会丢弃图标或上下文菜单或其项目,所以我实际上应该保留对所有内容的引用,或者至少有一个List<IDisposable>

或者我可以假设,因为垃圾收集器会为我调用这些处理器的范围(当我处理NotifyIcon时)?

4 个答案:

答案 0 :(得分:2)

这很大程度上取决于场景。例如,在许多UI框架中,向父控件/容器添加类似菜单项或图标的内容会使父承担该项的责任。因此,处理父母将同时处置所有祖先。所以:如果我们假设您正在创建的这些新控件将添加到UI中,则不会:您通常不需要手动执行任何操作。但是,如果您经常添加/删除控件,那么您将需要弄清楚何时取得元素的所有权(即如果您从UI中删除它,它现在是“你的”,而不是用户界面 - 处理它是你的责任。)

答案 1 :(得分:0)

通常,这就是using语句的用途,例如:

using (var icon = new Icon(...))
{
}

using语句一旦结束就调用Dispose方法(即使里面有异常,它仍然可以正确处理变量)。您甚至可以“链接”它们以减少嵌套:

using (var icon = new Icon(""))
using (var icon2 = new Icon(""))
using (var icon3 = new Icon(""))
{

}

您想尽快调用Dispose。如果不这样做,它仍然会在某个时刻被调用,通常是在对象的最终确定期间,但这可能已经太晚了(例如,处理文件句柄将允许打开另一个文件句柄,而如果你不要很快处理它,第二次调用将抛出异常)。

首先是Dispose方法的原因是因为Icon在后台有一些本机资源(它使用本机Icon对象,这都与Windows和GDI +有关)。这些资源“稀缺”(更好的例子是文件句柄),因此您不希望等待GC最终收集它们 - 这样做成本更高(如果广泛完成,最终确定会产生非常重要的成本)和可以阻止对资源的访问(即使使用Icon,我还记得每个进程只能分配这么多原生位图的问题,然后你就饿了。)

如果你不关心费用,如果资源不够重要,如果usingDispose比你的价值更麻烦 - 你不会必须调用Dispose。它最终将被调用。

作为你应该手动调用dispose的例子:文件,流,套接字,大块本机内存。

答案 2 :(得分:0)

来自MSDN:`

  

当创建新的NotifyIcon时,... [it]将存在,直到其容器将其释放到垃圾回收。

如果使用带有System.ComponentModel.Container所拥有的容器(通常为Form)的构造函数,则在处置容器时(即表单关闭时)将调用Dispose。 / p>

上面链接的MSDN文档中的示例以这种方式使用Form的容器。

如果您使用默认构造函数,则没有容器,您必须自己管理NotifyIcon的生命周期,并在完成后将其处置。

答案 3 :(得分:-2)

垃圾收集器最终将为所有对象调用Dispose(假设它们是根据IDisposable指南实现的),但作为最佳实践,您应该始终自己这样做。这只是一种最佳实践,它还可以让您检测可能存在问题的区域(例如,大型Image在内存中保留的时间太长 - 因为您将手动处理它,您的应用程序将抛出异​​常,如果它试图在你不打算的地方使用它。)

using (var icon = new Icon(...))
using (var contextMenu = new ContextMenu(...))
using (var contextMenuButton1 = new MenuItem(...))
using (var contextMenuButton2 = new MenuItem(...))
using (var contextMenuButton3 = new MenuItem(...))
{
    // ...
}

this.notifyIcon = new NotifyIcon(...);

由于您要存储(我在此代码示例中假设)this.notifyIcon值,您的类还必须实现IDisposable并在那里调用this.notifyIcon.Dispose()

编辑:对于那些说GC不会调用Dispose()的注释 - 实现IDisposable的标准模式是实现调用this.Dispose()的析构函数 - 因为GC确实调用了析构函数/ finalizer它也会间接调用Dispose()http://msdn.microsoft.com/en-us/library/ms244737.aspx