1

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

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

4

4 回答 4

2

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

不,那是一个神话。“破坏”这个词是完全不恰当的,这表明程序实际上努力对变量做一些特殊的事情以调用破坏。就像把一块砖扔进玻璃窗一样。这不是它的工作原理,变量只是凭空消失了。它被遗忘了,就好像它从未存在过一样。没有砖被扔。最终变量的存储被其他东西覆盖,它被重用。通常在几分之一微秒内。

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

扔砖需要使用using语句。

于 2013-11-09T15:28:02.707 回答
2

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

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

这种说法是错误的。

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

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

在类的情况下Brush,它通过 GDI API 创建一个系统对象。为了通过 GDI API 绘制填充图形,您必须调用一个方法来创建画笔对象。API 返回画笔对象的句柄,然后您可以使用该句柄在以后的 API 调用中引用该画笔。当您完成画笔后,您应该调用DeleteObjectAPI 调用以删除对象。由于 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
于 2013-11-09T15:33:44.137 回答
1

即使局部变量被破坏,这只是对位于托管堆上的 Brush 对象的引用。直到垃圾收集器清扫托管堆,实际对象才被销毁并释放资源。

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

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

using(var brush = CreateBrush())
{
   brush.PaintSomethingNice();
}
于 2013-11-09T15:22:40.503 回答
1

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

于 2013-11-09T15:22:56.507 回答