我是否需要在托管对象上调用Dispose()?

时间:2010-03-30 20:57:07

标签: .net idisposable

我无法相信我仍然对此感到困惑,但无论如何,让我们最终指出它:

我有一个类覆盖OnPaint来做一些绘图。为了加快速度,我先在构造函数中创建笔,画笔等,以便OnPaint不需要继续创建和处理它们。

现在,我确保我总是处理这些对象,但我感觉我不需要,因为尽管它们实现了IDisposable,但它们都是托管对象。

这是对的吗?


感谢所有答案,这个问题肯定已被钉上了 我很高兴我一直保持警惕,总是使用'​​使用',所以我不需要经历所有的代码检查。我只想清楚我不是一个毫无意义的用户。

顺便说一下,我确实有一个奇怪的情况,最近,我不得不更换一个使用块并手动调用dispose!我会把它挖出来并创建一个新问题。

12 个答案:

答案 0 :(得分:43)

正确。您需要处置实现IDisposable的对象。这就是为什么他们实现IDisposable - 来指定它们(直接或间接)包装非托管资源的事实。

在这种情况下,非托管资源是一个GDI句柄,如果在实际使用它们时未能处理它们,则会泄漏这些句柄。现在这些特定的对象具有终结器,这将导致在GC启动时释放资源,但您无法知道何时会发生这种情况。从现在开始可能是10秒,可能是10天后;如果你的应用程序没有产生足够的内存压力导致GC启动并在那些画笔/笔/字体/等上运行终结器,那么在GC实现正在发生的事情之前,你最终可能会使GDI资源的操作系统挨饿。

此外,您无法保证每个非托管包装器实际上都实现了终结器。 .NET Framework本身非常一致,因为实现IDisposable的类使用correct pattern实现它,但是某些其他类完全有可能实现破坏这不包括终结器,因此除非在其上明确调用Dispose,否则不会正确清理。一般来说,IDisposable的目的是你不应该知道或关心具体的实施细节;相反,如果它是一次性的,那么你处理它,期间。

故事的道德:始终处置IDisposable个对象。如果您的班级“拥有”IDisposable的对象,那么它应该自己实现IDisposable

答案 1 :(得分:9)

你需要处理它们。

托管对象自行管理内存。但是内存不是对象使用的唯一资源。 Dispose()旨在释放其他资源。

答案 2 :(得分:5)

你的方法有明显的讽刺意味。通过预先创建笔/画笔,您正好创建 Dispose()尝试解决的问题。那些GDI对象将会更长,就像你不调用Dispose()时一样。事实上更糟糕的是,它们至少会在表格关闭之前出现。

它们可能已经足够长,可以晋升为第二代。垃圾收集器不经常进行第2代收集,现在更多对它们调用Dispose()非常重要。通过将表单的Dispose()方法从Designer.cs文件移动到form.cs文件并添加Dispose调用来完成此操作。

但是,这是正确的方法。钢笔和刷子是非常便宜的物体。在Paint事件中,在需要时创建它们。并使用using语句,以便它们立即处理。使用秒表类重新确保自己,这实际上不会导致任何减速。

答案 3 :(得分:3)

我写了一个GDI +图表组件,它使用了很多笔和画笔。我创建了它们并将它们放在正在进行绘制的代码块中,性能从来都不是问题。更好的是,在OS IMHO中有一个长寿的句柄。

答案 4 :(得分:2)

您是否对此进行了分析,以了解是否创建&处理这些物体真的是个问题吗?我认为不是。

通过遵循使用块模式创建,您可以使自己的事情变得更容易,而且更容易出错。

如果您确实想要创建它们一次,那么还要在您自己的类上实现IDisposable,并在您拥有的对象上迭代Dispose。不需要析构函数(终结器)。

对于实际上不需要Dispose的对象几乎没有成本,但如果忘记对需要它的对象进行Dispose,则会产生很大的代价。

答案 5 :(得分:1)

不,IDisposable适用于使用非托管资源的托管对象。 通常,你应该在完成后处理它们。

答案 6 :(得分:1)

你真的需要查看画笔,笔等的文档。

如果他们没有使用非托管资源,您可能不必调用Dispose。但是使用/ Dispose模式有时会被“误用”。作为示例,请考虑ASP.NET MVC框架。在这里你可以写出类似的东西:

using(Html.BeginForm(...)){
  ///HTML input fields etc.
}

调用Html.BeginForm(...)时,将输出FORM标记。当using语句结束时,将DisposeHtml.BeginForm(...)返回的对象上调用Dispose。调用FORM会导致呈现结束{{1}}标记。通过这种方式,编译器将实际强制执行FORM标记的配对,因此您不会忘记结束标记。

答案 7 :(得分:1)

不,PenBrush es 不是完全管理的对象。

它们包含非托管资源的句柄,即底层图形系统中的相应GDI对象。 (不确定这里的确切术语......)

如果你没有丢弃它们,那么在垃圾收集器完成对象之前不会释放把手,并且不能保证它很快就会发生,或者根本不会发生。

答案 8 :(得分:1)

没错。我同意Aaronaught的观点。

此外,Microsoft在2003年中期的Don Box网络广播中建议,每个.Net开发人员都应该处理自己的对象,无论是托管还是非托管,因为这可以将代码性能提高20%。如果做得好,它可以显着改善性能。因此它是每个.net开发人员需要知道和使用的核心技能。

答案 9 :(得分:1)

其他人已经提到“使用”块用于GDI对象 - 这是一个代码示例:

using( var bm = new Bitmap() )
using( var brush = new Brush() )
{

   // code that uses the GDI objects goes here
   ...

} // objects are automatically disposed here, even if there's an exception

请注意,您可以为单个代码块添加任意数量的“使用”行。

我认为这是一种处理Disposable对象的好方法。

答案 10 :(得分:0)

虽然您询问过钢笔和画笔,但Font是一个有一些奇怪怪癖的类。特别是,如果为了设置控件的Font属性而创建一个字体,则仍然负责处理该字体 - 所有权不会转移到控件 - 但是可以通过处理该字体来执行该任务。任何时候 - 即使一旦创建了字体,在将其分配给控件之前。似乎Font是托管信息对象和非托管GDI资源的组合,出于某些目的,只需要前者。奇怪的设计 - 字体应该是两个类。

答案 11 :(得分:0)

在类似的帖子上看到我的回答。如果你不打电话给Dispose,它应该解决你的问题并解释其后果。

What happens if I dont call Dispose on the Pen object