代码之家  ›  专栏  ›  技术社区  ›  David Gardiner

避免处理系统定义的笔和画笔实例

  •  11
  • David Gardiner  · 技术社区  · 16 年前

    我了解对的实例调用Dispose()是最佳实践。 Pen Brush ,除非它们已设置为系统预定义值(例如 System.Drawing.Brushes , System.Drawing.Pens System.Drawing.SystemBrushes )

    尝试释放系统定义的资源会导致引发异常。

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

    8 回复  |  直到 13 年前
        1
  •  -9
  •   AMissico    16 年前

    不需要打电话 Dispose . 垃圾收集的目的是消除这些需求。

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

    如果您“必须”调用Dispose,但不知道Brush实例是“System-”还是“Normal-”Brush,则必须使用Try…Catch块。

    • 不需要调用Dispose on SystemBrushes SystemPens 因为gdi+库将处理这些资源。
    • 可以处理 SystemFonts SystemIcons .

    这个 评论 如果需要调用 处置 方法。 如果“备注”部分建议调用Dispose方法,那么我将执行该操作。

    一般来说,我不叫“处理”笔和刷子。如果我有一个图形密集型应用程序或类,那么我将缓存我需要的笔和画笔的实例。我在整个应用程序或类的生命周期中使用它们。如果我不这样做,那么图形绘画的表现将受到尝试创造和处理所有这些作品的多次,如此频繁。(嗯……现在我想起来了,性能可能就是为什么我们不能处理SystemBrush和SystemPens,但是可以处理SystemFonts和SystemIcons的原因。甚至框架也会缓存SystemBrush和SystemPens。)

        2
  •  30
  •   Eric Lippert    13 年前

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

    不需要调用Dispose。

    这句话有误导性。尽管从技术上讲你可以不打电话就走了 Dispose ,不丢弃刷子或笔是一种非常糟糕的做法。 你拥有的 一旦你完成了。

    原因是:有一个硬限制(默认设置为10000,尽管可以通过注册表黑客增加)可以在一个进程(而不是应用程序域,注意)中未完成的gdi对象的数量,垃圾收集器可能无法更快地完成资源泄漏。托管内存分配产生 收集压力 但是没有对应的 定型压力 .

    垃圾收集的目的是消除这些需求。

    不是这样。垃圾收集的目的是模拟具有任意多内存的环境。

    其主要目的之一是 IDisposable 允许类在资源有限的环境中清除非托管资源。

    这是正确的。

    如果你不打电话给 Dispose() 方法,一旦对象在垃圾收集期间被定义和处理,类的非托管资源将被清除。

    这有时是正确的;对于gdi对象是正确的。对于那些依赖于非托管资源的类来说,将终结语义实现为一个后盾是一个很好的实践;对于遵循公认答案中给出的错误建议的人来说,这是一个额外的保护层。 你不应该依靠终结器来避免你的错误;你应该处理你的资源。

    你应该只依靠一个终结器来处理疯狂的特殊情况。例如,考虑:

    Brush myBrush = MakeMeABrush();
    DoSomethingWithBrush(myBrush);
    myBrush.Dispose();
    

    假设引发了线程中止异常 之后 刷子的配置 之前 分配给我的画笔。无组合 try-catch-finally 能让你通过 处置 你必须依靠定稿机。这就是定稿人的目的: 完全疯狂 你的情况 不能 处理好自己。 这不是马虎的借口。

    如果您“必须”调用Dispose,但不知道Brush实例是“System-”还是“Normal-”Brush,则必须使用Try…Catch块。

    尽管这在技术上是正确的,但它完全没有抓住要点。 如果您处于一种不知道是否拥有画笔的情况下,那么您的程序设计中就有一个bug。 . 不要用 try-catch 阻止!修复错误!

    这种情况对于所有显式管理的资源都很常见: 提供资源的实体和消耗资源的实体负责清楚地记录两个实体中谁拥有清理资源的权限。 . 如果你不知道你是否拥有你得到的刷子,那么 有人没有做他们有责任做的事情 ,即 防止这种情况的发生 .

    如果您决定的合同是提供资源的实体负责稍后清理资源,则 你的消费者根本不应该丢弃刷子。 因为这违反了合同;如果生产商需要的话,他们会清理掉的。

    如果你决定的合同是生产者和消费者都要释放资源,那么消费者必须打电话给 Clone 每一个 刷子传递进来以确保它们有一个安全的可释放资源,并且生产者仍然拥有一个有效的资源。

    如果,最可能的情况是,您决定的合同是消耗资源的实体负责稍后清理资源,那么 供应商必须始终为您提供一个可以安全处置的刷子。 ,并且必须 释放资源本身。自从 供应商 知道它们是自己制作的还是从系统中获取的,提供程序必须调用 Clone() 在系统刷上获取可安全处置的刷,然后通过 那个 给消费者。

    但“没有人能把它清理干净,我们希望最好”,这是一份相当糟糕的合同。

    不需要打电话 处置() SystemBrushes SystemPens 因为gdi+库将处理这些资源。

    这个解释实际上并不能解释任何事情。处置其中一个刷子是违法的原因是 画笔的生存期等于AppDomain的生存期 .

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

    这句话是假的。你应该假设总是有必要 处置 不可分的 资源,除非你有充分的理由相信。文件中没有一行并不能证明不需要处理。

    以积极的态度结束:

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

    这是一个很好的做法。如果所创建的画笔和笔的数量相对较小,并且相同的画笔被反复使用,那么永久缓存它们是很有意义的。因为它们的生命周期到了程序的末尾,所以不需要处理它们。在这种情况下,我们不会处理垃圾,因为 不是垃圾 这很有用。gdi对象 当然,应该仍然释放缓存的。同样,追求缓存策略并不是从事不良实践的借口。

        3
  •  4
  •   Bug    13 年前

    完成创建的图形对象后,必须通过调用其Dispose方法来释放它。(此规则适用于许多不同的gdi+对象。)不要将其保留在雨天,因为它稍后将无效。你必须,必须,必须,必须处理它当你完成它。如果不这样做,可能会导致图像损坏、内存使用问题,或者更糟的是,国际武装冲突。因此,请妥善处理所有图形对象。

    如果您在一个事件中创建了一个图形对象,那么在退出该事件处理程序之前,您确实需要对其进行处理。不能保证图形对象在以后的事件中仍然有效。此外,很容易在任何时候重新创建另一个图形对象。

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

        4
  •  2
  •   Ari Roth    16 年前

    首先,你总是应该在可以的时候处理掉刷子,而不是把它留给垃圾收集器。虽然GDI最终会处理好这些东西(假设图书馆被适当关闭),但并不能确定何时关闭。事实上,我的理解是刷柄可以长期使用。在长期运行的应用程序过程中,您将看到 事实上的 内存泄漏。当然,在一个不会运行很长时间的小应用程序中,或者在一个很少创建画笔的应用程序中,您可以让系统处理它,但这对我来说是草率的。

    作为一般规则,每当我创建一个画笔时,我都会在using语句中这样做。这会自动调用画笔上的Dispose,而不必担心它。另外,由于您在语句中创建了画笔,所以您知道它不是预定义的画笔。任何时候创建和使用非预定义的画笔时,都可以使用包装块,而不必担心它。实际上,您甚至不需要显式调用Dispose,因为即使在发生异常的情况下,块也会这样做。

        5
  •  2
  •   Daniel    14 年前

    简短的回答是,。如果你创造了它,要么委派责任去清理,要么自己清理。您可以通过让垃圾收集器中挂起的东西来创建GDI资源“泄漏”。它们最终可能会被清理干净,但挂在那里没有任何好处。也就是说,如果您不调用“close”或对打开的文件进行释放,则该文件将保持锁定状态,直到GC“找到它”。

        6
  •  0
  •   lzcd    16 年前

    我可能是错的,但我认为你可以假定预定义的画笔和笔的使用寿命(和处置)不是你的应用程序的责任,而是由系统来处理。

    简而言之:不要对预定义的东西调用Dispose。:)

        7
  •  0
  •   serhio    16 年前

    唯一需要考虑的是要有一个实践,不要使用系统笔/刷子作为方法的参数。

        8
  •  0
  •   David Gardiner    16 年前

    为了完整起见,使用Reflector,我看到System.Drawing.Pen和System.Drawing.SolidBrush类有一个名为“不可变”的私有字段。

    当对象引用系统定义的资源时,此字段似乎设置为true,因此可以使用反射仔细检查此字段的值以决定是否对其调用Dispose()。

    推荐文章