是否需要处理本地刷变量?

时间:2013-11-09 15:18:36

标签: c# .net vb.net winforms visual-studio

MSDN建议在释放最后一个引用之前处理System.Drawing.Brush类型的任何变量。否则,在垃圾回收器调用Brush对象的Finalize方法之前,不会释放它正在使用的资源。

众所周知,当控制流超出其所属方法的范围时,会自动销毁局部变量。那么,如果它是本地的,每次都需要处理一个画笔对象吗?

4 个答案:

答案 0 :(得分:2)

  

众所周知,局部变量会自动销毁

不,这是一个神话。 “破坏”这个词完全不合适,这表明该程序实际上是在努力对变量做一些特殊的事情来调用破坏。就像把砖扔进玻璃窗里一样。这不是它的工作方式,变量只是在空气中消失。它被遗忘了,好像它从未存在过一样。没有扔砖头。最终,变量的存储被其他东西覆盖,重用。通常只需几微秒。

不必销毁变量就是托管代码与本机代码竞争的原因。例如,C ++编译器必须明确地执行它,RAII模式是样板文件。使用引用计数的较旧的运行时实现是另一个示例,它们必须确保明确地对引用计数进行倒计时。这是托管代码不需要的额外代码,垃圾收集器知道何时使用本地变量。完成这项工作只是有点慢,IDisposable存在的原因。尝试将引用计数添加到CLR是abysmal failure,它无法与GC竞争。

使用 using 语句需要抛出砖块。

答案 1 :(得分:2)

是的,这是必要的。你的逻辑中的缺陷是当你说:

  

众所周知,当控制流超出其所属方法的范围时,局部变量会自动销毁。

该陈述是错误的。

当变量超出范围时,这意味着它们是未来某个时候被垃圾收集器销毁的候选者,但很可能不会立即消失。因此,如果立即释放系统资源很重要,那么您需要手动执行此操作,而不是等待垃圾收集器在将来某个时间进行操作。

这就是使用系统资源的类实现IDisposable接口的原因。他们希望您在完成它们时调用他们的Dispose方法,以便他们可以立即释放系统资源。可以安全地假设您应该始终在实现Dispose接口的每个对象上调用IDisposable方法。如果没有必要,他们就不会实现该接口。

Brush类的情况下,它通过GDI API创建系统对象。要通过GDI API绘制填充图形,必须调用方法来创建画笔对象。 API返回画笔对象的句柄,然后您可以使用该句柄在以后的API调用中引用该画笔。完成刷子后,您应该调用DeleteObject API调用以删除对象。由于Windows中的每个进程限制为最多10,000个GDI对象,因此在完成它们时删除它们非常重要,否则您将耗尽GDI对象并导致OutOfMemoryException。这就是Brush类实现IDisposable接口的原因 - 这样它就可以删除底层的GDI对象。

建议尽可能在所有一次性物品上使用using块。当执行离开using块时,它将自动为您调用对象上的Dispose方法,即使执行因异常而离开了块。

using(Brush b = New Brush())
{
    // use the brush
}

或者

Using b As New Brush()
    ' use the brush
End Using

答案 2 :(得分:1)

即使本地变量被销毁,这只是对Brush对象的引用,该对象位于托管堆中。直到垃圾收集器扫描托管堆,才会销毁实际对象并释放资源。

作为一般规则:当您完成对象时,始终在实现Dispose的任何对象上调用IDisposable

最好的方法通常是使用using构造,即使在例外的情况下也会正确处理:

using(var brush = CreateBrush())
{
   brush.PaintSomethingNice();
}

答案 3 :(得分:1)

是的,这是必要的。一般来说,如果它实现IDisposable,你应该在完成后处理它。当它超出范围时,这只意味着它有资格进行垃圾收集。 GC可能不会长时间处理它,所以你应该立即处理它。