MSDN 建议在释放最后一个引用之前处理任何 System.Drawing.Brush 类型的变量。否则,在垃圾收集器调用 Brush 对象的 Finalize 方法之前,它正在使用的资源不会被释放。
众所周知,当控制流超出其所属方法的范围时,局部变量会自动销毁。那么,如果是局部的,是否每次都需要处理一个画笔对象呢?
MSDN 建议在释放最后一个引用之前处理任何 System.Drawing.Brush 类型的变量。否则,在垃圾收集器调用 Brush 对象的 Finalize 方法之前,它正在使用的资源不会被释放。
众所周知,当控制流超出其所属方法的范围时,局部变量会自动销毁。那么,如果是局部的,是否每次都需要处理一个画笔对象呢?
众所周知,局部变量会自动销毁
不,那是一个神话。“破坏”这个词是完全不恰当的,这表明程序实际上努力对变量做一些特殊的事情以调用破坏。就像把一块砖扔进玻璃窗一样。这不是它的工作原理,变量只是凭空消失了。它被遗忘了,就好像它从未存在过一样。没有砖被扔。最终变量的存储被其他东西覆盖,它被重用。通常在几分之一微秒内。
不必破坏变量是托管代码与本机代码竞争的原因。例如,C++ 编译器必须明确地执行此操作,RAII 模式是样板文件。使用引用计数的较旧的运行时实现是另一个示例,它们必须确保显式地对引用计数进行倒计时。这是托管代码不需要的额外代码,垃圾收集器知道何时使用局部变量。完成这项工作有点慢,这是 IDisposable 存在的原因。将引用计数添加到 CLR 的尝试非常失败,它无法与 GC 竞争。
扔砖需要使用using语句。
是的,这是必要的。你的逻辑缺陷是当你说:
众所周知,当控制流超出其所属方法的范围时,局部变量会自动销毁。
这种说法是错误的。
当变量超出范围时,这意味着它们是垃圾收集器在未来某个时间点销毁的候选对象,但很可能不会立即销毁。因此,如果立即释放系统资源很重要,您需要手动执行此操作,而不是等待垃圾收集器在将来的某个时间开始执行此操作。
这就是使用系统资源的类实现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
即使局部变量被破坏,这只是对位于托管堆上的 Brush 对象的引用。直到垃圾收集器清扫托管堆,实际对象才被销毁并释放资源。
作为一般规则:当您完成对象时,始终调用Dispose
任何实现的对象。IDisposable
最好的方法通常是使用using
即使在异常情况下也能正确处理的构造:
using(var brush = CreateBrush())
{
brush.PaintSomethingNice();
}
是的,这是必要的。一般来说,如果它实现了IDisposable
,你应该在完成后处理它。当它超出范围时,这仅意味着它有资格进行垃圾收集。GC可能很长一段时间都不会处理它,所以你应该立即处理它。