避免配置系统定义的Pen和Brush实例

Dav*_*ner 11 system.drawing gdi+ idisposable winforms

我理解最好在PenBrush实例上调用Dispose(),除非它们已经设置为系统预定义值(例如System.Drawing.Brushes, System.Drawing.PensSystem.Drawing). SystemBrushes)

尝试处置系统定义的资源会导致抛出异常.

除了在try/catch中包装Dispose()调用之外,这些资源中的一个是否引用了系统定义的值或用户定义的值,这似乎并不明显.

Eri*_*ert 31

这是我所知道的一个老问题,但我一直在研究涉及GDI对象的资源泄漏,并且几乎接受的答案中的每个语句都是错误的.为了通过搜索找到这个问题的后来读者的准确性,正如我所做的那样:

无需调用Dispose.

这种说法具有误导性.虽然从技术上讲,你可以逃避不打电话Dispose,但是一旦你完成它就不会丢弃你拥有的刷子或笔,这是一种极其糟糕的做法.

原因是:存在一个硬限制(默认情况下设置为一万,尽管可以通过注册表黑客增加)可以在流程中突出的GDI对象数量 - 而不是应用程序域,注意 - 以及垃圾收集器可能无法比泄漏更快地完成资源的最终化.管理内存分配产生收集压力,但没有相应的最终确定压力.

垃圾收集的目的是消除这些要求.

它不是.垃圾收集的目的是模拟具有任意大量内存的环境.

其中一个主要目的IDisposable是允许类在资源有限的环境中清理非托管资源.

这是对的.

如果不调用该Dispose()方法,则在对象完成初始化并在垃圾回收期间处理时,将清除该类的非托管资源.

这有时是正确的; 它对GDI对象是正确的.对于持有非托管资源的类来说,实现最终化语义作为后备是一种很好的做法; 为那些遵循接受的答案中给出的错误建议的人提供额外的保护. 你不应该依赖终结者来避免你的错误; 你应该处置你的资源.

你应该只依靠终结者来应对疯狂的异常情况.考虑例如:

Brush myBrush = MakeMeABrush();
DoSomethingWithBrush(myBrush);
myBrush.Dispose();
Run Code Online (Sandbox Code Playgroud)

假设在刷子分配之后分配给myBrush 之前抛出线程中止异常.没有任何组合try-catch-finally可以让你通过清理刷子Dispose; 你将不得不依赖终结者.这就是终结者的目的:完全疯狂的情况,你无法自我解决.这不是一个草率的借口.

如果您"必须"调用dispose并且您不知道画笔实例是"系统"还是"普通"画笔,那么您将不得不使用try ... catch块.

虽然这在技术上是正确的,但它完全忽略了这一点.如果您处于不知道自己是否拥有画笔的情况,那么您的程序设计就会出现错误.不要用try-catch块来破坏你的bug !修复错误!

这种情况对于所有明确管理的资源是共同的:提供资源的实体和消耗资源的实体负责清楚地记录两个拥有哪个资源来清理资源.如果你不知道你是否拥有你曾经给过的刷子,那么有人不会做他们负责的任务,即防止这种情况发生.

如果您决定的合同是提供资源的实体负责清理它,那么您的消费者根本不应该处理刷子,因为这违反了合同; 如果需要,生产者将清理它.

如果您决定在合同是双方生产者和消费者将要释放资源,那么消费者必须调用Clone每一个传入刷,以确保他们有一个安全的一次性资源,而生产者继续拥有一个有效的资源同样.

如果您决定的合同很可能是消耗资源的实体负责在以后清理它,那么提供者必须总是递给您一个可以安全处理的刷子,并且要求处理资源本身.由于提供者知道他们是自己制作刷子还是从系统Clone()中获取刷子,因此提供者必须调用系统刷子以获得可以安全处理的刷子,然后将传递给消费者.

但合同"没有人清理它,我们希望最好的"是一个非常糟糕的合同.

它不需要调用Dispose()SystemBrushes,并SystemPens因为GDI +库将利用这些资源的照顾.

这种解释是一个实际上没有解释的解释.处理这些刷子之一是非法的原因是因为刷子的寿命等于appdomain的寿命.

该类的备注部分将记录是否需要调用Dispose方法.

这句话是错误的.你应该作一个假设,它始终是必要DisposeIDisposable资源,除非你有充足的理由不相信.文档中缺少一行并不表明处理是不必要的.

以积极的方式结束:

如果我有一个图形密集型应用程序或类,那么我将缓存我需要的笔和画笔的实例.我在应用程序或类的整个生命周期中使用它们.

这是一个很好的做法.如果创建的画笔和笔的数量相对较少并且一次又一次地使用相同的画笔和笔,那么永久地缓存它们是很有意义的.由于它们的生命周期到程序结束,因此无需处置它们.在这种情况下,我们不处理垃圾,因为它不是垃圾 ; 它很有用.当然,仍然应该处理缓存的GDI对象.同样,追求缓存策略并不是参与不良做法的借口.

  • @AMissico:你也继续以非常不幸的方式使用"要求".你基本上说没有*要求*程序员选择使用最佳实践编写程序; 程序员可以选择编写草率,危险,脆弱的代码.这在某种意义上是正确的; 尚未发明任何编程语言阻止开发人员编写错误的代码.但是,在"你可以侥幸逃脱"的基础上提倡*最糟糕的做法会让你的同事们受到伤害. (4认同)
  • @AMissico:那么你可能应该学会欣赏建设性的批评而不采取防御措施; 这样做对我的职业生涯有很大帮助,也可能对你有所帮助.你的态度是坏的,足够好,细节也无关紧要,也可能会有所调整.我不批评你的答案,因为它是*不完美*,我批评它,因为它正在积极地伤害世界.它很容易通过网络搜索找到,并且包含了那些不太了解的人真正非常糟糕的建议 - 如果他们知道更好的话,他们为什么会寻求建议呢? - 可能需要. (4认同)
  • @supercat:你错了`.Font`赋值不会触发处理.Hans Passant在回复http://stackoverflow.com/q/5148476/18192时对此进行了讨论.检查.Net参考源代码中的Control.cs确认Hans是正确的; 有一些代码可以确保`Font`在`OnFontChanged`函数和`Font` setter中都有.这些`dispose`调用并不总是发生(例如,如果你自己分配一个Font,它就不会处理它,因为它显然在使用中). (2认同)
  • @AMissico:缺少编译器错误源于这样的事实:在一般情况下,编译器不可能知道最后会使用哪个对象引用.如果"IDisposable"被包含在框架的核心中,而不是事后的想法,那么这些事情就会得到更好的处理.但是,实际上,允许放弃实现`IDisposable`的对象而没有调用它们的`Dispose`而且没有充分理由这样做的代码被认为是破坏的代码. (2认同)

小智 5

当您完成创建的 Graphics 对象后,必须通过调用其 Dispose 方法来处理它。(这条规则对于许多不同的 GDI+ 对象都是正确的。)不要保留它以备不时之需,因为它以后将不再有效。当你用完它后,你必须、必须、必须把它处理掉。如果不这样做,可能会导致图像损坏、内存使用问题,或更糟糕的是,导致国际武装冲突。因此,请正确处理所有 Graphics 对象。

如果您在事件中创建 Graphics 对象,则确实需要在退出该事件处理程序之前处理它。无法保证 Graphics 对象在以后的事件中仍然有效。此外,随时可以轻松地重新创建另一个 Graphics 对象。

从这里获取 http://msdn.microsoft.com/en-us/library/orm-9780596518431-01-18.aspx


AMi*_*ico -9

没有要求必须打电话Dispose。垃圾收集的目的就是消除这些要求。

的主要目的之一IDisposable是允许类在资源有限的环境中清理非托管资源。如果不调用 dispose 方法,则一旦对象在垃圾收集期间完成并释放,该类的非托管资源将被清除。

如果您“必须”调用 dispose 并且您不知道画笔实例是“系统”还是“普通”画笔,那么您将不得不使用 try...catch 块。

  • 不需要调用 dispose SystemBrushesSystemPens因为 GDI+ 库将处理这些资源。
  • 可以处理掉SystemFontsSystemIcons

类的备注部分注明是否需要调用该Dispose方法。如果备注部分建议调用 Dispose 方法,那么我会这样做。

一般来说,我不会对钢笔和画笔进行废弃处理。如果我有一个图形密集型应用程序或类,那么我将缓存我需要的笔和画笔的实例。我在应用程序或类的整个生命周期中使用它们。如果我不这样做,那么尝试多次且频繁地创建和处理所有这些对象时,图形绘制性能将会受到影响。(嗯……现在我想了一下,性能可能就是为什么我们不能处理SystemBrushes和SystemPens,但却可以处理SystemFonts和SystemIcons。甚至框架也缓存了SystemBrushes和SystemPens。)

  • @DavidGardiner:您会考虑不接受这个答案,这样它就不再固定在页面顶部,等待误导更多的读者吗? (2认同)