MSDN建议在释放最后一个引用之前处理System.Drawing.Brush类型的任何变量。否则,在垃圾回收器调用Brush对象的Finalize方法之前,不会释放它正在使用的资源。
众所周知,当控制流超出其所属方法的范围时,会自动销毁局部变量。那么,如果它是本地的,每次都需要处理一个画笔对象吗?
答案 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可能不会长时间处理它,所以你应该立即处理它。