据我所知,最佳实践是在使用Pen和Brush的实例时调用Dispose()方法,但若它们已被设置为系统预定义值(例如System.Drawing.Brushes、System.Drawing.Pens或System.Drawing.SystemBrushes),则无需这样做。
试图释放系统定义的资源会导致抛出异常。
除了将Dispose()调用包装在try/catch中之外,似乎不太容易检测到这些资源是引用系统定义的还是用户定义的值。
据我所知,最佳实践是在使用Pen和Brush的实例时调用Dispose()方法,但若它们已被设置为系统预定义值(例如System.Drawing.Brushes、System.Drawing.Pens或System.Drawing.SystemBrushes),则无需这样做。
试图释放系统定义的资源会导致抛出异常。
除了将Dispose()调用包装在try/catch中之外,似乎不太容易检测到这些资源是引用系统定义的还是用户定义的值。
这是一个旧问题,我知道,但是我一直在研究涉及GDI对象的资源泄漏,并且几乎接受的答案中的每个声明都是错误的。为了确保以后通过搜索找到此问题的读者的准确性,就像我一样:
没有必要调用Dispose。
这种说法是误导性的。虽然技术上您可以不调用Dispose
,但是不处理自己拥有的画笔或钢笔是一种非常糟糕的实践。
原因是:进程中可以使用的GDI对象数量有一个硬限制(默认设置为一万,尽管可以通过注册表修改增加),垃圾收集器可能无法在资源泄漏时更快地完成资源的终结。 管理的内存分配产生收集压力,但没有相应的终结压力。
垃圾回收的目的是消除这些要求。
不是这样的。垃圾集合的目的是模拟具有任意多内存的环境。
IDisposable
的主要目的之一是允许类在资源有限的环境中清理非托管资源。
这是正确的。
如果您没有调用
Dispose()
方法,则该类的非托管资源将在垃圾回收期间对象被终结和释放时得到清理。
这有时是正确的;对于GDI对象,这是正确的。对于持有非托管资源的类来说,实现终止语义作为后备措施是一种好的实践;这是为遵循接受的答案中给出的错误建议的人提供的额外保护层。您不应该依赖终结器来拯救您的错误;您应该处理自己的资源。
只有在疯狂的例外情况下才应依赖终结器。例如:
Brush myBrush = MakeMeABrush();
DoSomethingWithBrush(myBrush);
myBrush.Dispose();
假设在线程分配了笔刷之后,但在将笔刷赋值给myBrush之前,引发了线程中止异常。无论如何使用try-catch-finally
的组合,都无法通过Dispose
清理那支笔刷;你必须依靠终结器。这就是终结器存在的原因:在完全疯狂的情况下,你无法自我处理的异常情况。 这不是懒散的借口。
虽然从技术上讲这是正确的,但它完全忽略了问题的关键。如果您处于不知道是否拥有该笔刷的情况下,则程序设计中存在错误。不要用如果您“必须”调用dispose并且不知道brush实例是“系统 brush”还是“普通brush”,那么就必须使用try...catch块。
try-catch
块掩盖错误!解决问题!Clone
,以确保它们拥有可安全处理的资源,并且生产者仍然拥有有效的资源。如果你选择的合同是消费资源的实体负责稍后清理它,那么服务提供商需要始终为您提供一个可以安全处理的画笔,并且不能自行处理该资源。由于提供程序知道他们是自己制作的还是从系统中获取的画笔,因此提供程序必须在系统画笔上调用Clone()
以获得可以安全处理的画笔,然后将它传递给消费者。
但是“没有人打扫并且我们希望一切顺利”的合同非常糟糕。
不需要对
SystemBrushes
和SystemPens
调用Dispose()
,因为GDI+库会处理这些资源。
这种解释实际上并没有解释任何事情。之所以禁止处理其中之一的画笔,是因为画笔的生命周期等于应用程序域的生命周期。
类的备注部分会注明是否需要调用Dispose方法。
这个说法是错误的。您应该假设除非您有充分的理由相信不需要处理,否则始终需要Dispose
一个IDisposable
资源。文档中缺少一行并不意味着处理不必要。
为了以积极的姿态结束:
如果我有一个图形密集型应用程序或类,那么我将缓存所需的笔和画笔实例。我在整个应用程序或类的生命周期内使用它们。
这是一个好的做法。如果创建的画笔和笔刷数量相对较少,并且一直在重复使用相同的画笔和笔刷,则将它们永久缓存是很有意义的。由于它们的生命周期是到程序结束为止,所以不需要处理它们。在这种情况下,我们不会处理垃圾,因为它不是垃圾,而是有用的。当然,没有被缓存的GDI对象应该仍然被处理。再次强调,追求缓存策略并不是进行不良实践的借口。
简短的回答是,如果您创建了它,请委托负责清理或自己清理对象。如果让东西挂在垃圾收集器中,就可能会创建GDI资源“泄漏”。它们最终可能会被清除,但是挂在那里没有任何好处。例如,如果不在打开的文件上调用“close”或Dispose,则文件将保持锁定状态,直到GC“处理完它为止”。
我可能错了,但我认为您可以假设预定义的画笔和笔刷的生命周期(以及处理)不是您应用程序的责任,而将由系统处理。
简而言之:不要在预定义的内容上调用Dispose。 :)
唯一想到的是练习时不要将系统笔刷作为方法参数。
Dispose()
。如果调用者获取了该对象,则知道它是从系统画刷获取的还是其他,可以相应地包含或省略 using
块。 - binki没有必要调用Dispose
方法。垃圾回收的目的是为了消除这些要求。
IDisposable
的主要目的之一是允许类在资源有限的环境中清理非托管资源。如果您不调用dispose方法,则在对象被终结和在垃圾回收期间处理时,该类的非托管资源将被清理。
如果您“必须”调用dispose并且不知道刷子实例是“系统”还是“普通”刷子,则必须使用try...catch块。
SystemBrushes
和SystemPens
调用dispose,因为GDI+库会处理这些资源。SystemFonts
和SystemIcons
进行处理。类的备注部分将说明是否需要调用Dispose
方法。如果备注部分建议调用Dispose方法,则我会这样做。
IDisposable
接口,因为它们使用非托管资源。因此,在这个问题和答案中,Dispose(disposing=False)
将始终通过Finalize
方法调用。尽管如此,仍然没有必要调用Dispose
。 - AMissicoSystemBrushes
和SystemPens
对象的Dispose
方法。 - AMissico
.Font
赋值不会触发处理的说法是错误的。Hans Passant在回答https://dev59.com/IlTTa4cB1Zd3GeqPvcRu时讨论了这个问题。检查.Net参考源代码中的Control.cs可以证实Hans是正确的;在`OnFontChanged`函数和`Font` setter中都有代码来确保Font
被处理。这些dispose
调用并不总是发生(例如,如果你将一个字体分配给自己,它不会被处理,因为显然正在使用)。 - BrianIDisposable
包含在框架的核心中,而不是作为事后想法,这些问题将得到更好的处理。然而,允许实现IDisposable
的对象被放弃而没有调用Dispose
的代码(除非有充分的理由),应被视为有缺陷的。 - supercatIDisposable
来处理那些资源必须手动清理,因为运行时无法很好地完成清理工作的情况,显然,编译器无法告诉您何时应该处理。如果它可以这样做,那么我们就不需要IDisposable
了! - Eric Lippert