设置 IDisposable 属性时调用 Dispose?

发布于 2024-11-29 12:22:01 字数 721 浏览 2 评论 0原文

我今天更改了 FxCop 规则,将任何未处理的 IDisposables 指出为错误,希望它可以帮助我追踪一些 GDI 泄漏。有趣的是,它向我指出了一个我不太确定如何处理的例子:

public class CustomGoRectangle : GoRectangle
{
   public void DoStuff()
   {
      Pen p = new Pen(Color.Red, 4.0f);
      this.Pen = p;
   }

   public void DoOtherStuff()
   {
      Pen p = new Pen(Color.Blue, 4.0f);
      this.Pen = p;
   }

   public void Test()
   {
      this.Pen = Pens.Green;
      DoStuff();
      DoOtherStuff();
   }
}

快速解释。 GoRectangle 位于第 3 方库中,并具有 Pen 属性,它不是 IDisposable。

我把笔放在了很多地方,就像上面的例子一样。这里说明的问题是,调用 DoStuff() 我创建了一支新笔,它从未被释放。现在,如果在分配新值之前 this.Pen 不为 null,我可以调用 dispose,但正如 Test() 所示,如果已设置系统笔,这只会导致进一步的问题。处理这种情况的实际模式是什么?

I switched the FxCop rule today to point out any non-disposed IDisposables as errors, in a hope that it might help me track down some GDI leaks. Interestingly it pointed me to an instance that I'm not quite sure how to deal with:

public class CustomGoRectangle : GoRectangle
{
   public void DoStuff()
   {
      Pen p = new Pen(Color.Red, 4.0f);
      this.Pen = p;
   }

   public void DoOtherStuff()
   {
      Pen p = new Pen(Color.Blue, 4.0f);
      this.Pen = p;
   }

   public void Test()
   {
      this.Pen = Pens.Green;
      DoStuff();
      DoOtherStuff();
   }
}

Quick explanation. GoRectangle is within a 3rd party library and has a Pen property, it isn't IDisposable.

I set my pen in a number of places, like the contrived example above. The problem illustrated here is that calling DoStuff() I create a new pen, which is never disposed. Now I could call dispose on this.Pen if it's not null before assigning a new value, but then as Test() illustrates if a System Pen has been set this will just cause further problems. What's the actual pattern for dealing with situations like this?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

誰認得朕 2024-12-06 12:22:01

您可以在重新分配之前手动调用 this.Pen.Dispose()

You could manually call this.Pen.Dispose() before reassigning it.

む无字情书 2024-12-06 12:22:01

不,您不能处置所有笔,因为如果您尝试处置它们,系统笔将抛出ArgumentException(我刚刚使用反射器进行了检查)。

但是,您可以使用反射来获取 Pen 中私有字段 immutable 的值。如果为false,您可以安全地处理笔。 (我不知道单声道是否以同样的方式工作)

这里有一个扩展方法可以帮助您:

public static void DisposeIfPossible(this Pen pen)
{
    var field = pen.GetType().GetField("immutable", BindingFlags.Instance|BindingFlags.NonPublic);
    if ((bool)field.GetValue(pen) == false)
        pen.Dispose();
}

在分配新笔之前调用它。

No, you can't dispose all pens since system pens will throw ArgumentException if you try to dispose them (I just checked using reflector).

However, you can use reflection to get the value of the private field immutable in the Pen. If it's false you can safely dispose the pen. (I do not know if mono works in the same way)

Here is an extension method to help you:

public static void DisposeIfPossible(this Pen pen)
{
    var field = pen.GetType().GetField("immutable", BindingFlags.Instance|BindingFlags.NonPublic);
    if ((bool)field.GetValue(pen) == false)
        pen.Dispose();
}

Call it before assigning a new pen.

暗地喜欢 2024-12-06 12:22:01

我通常处理此类事情的方式如下:

1)向图书馆提供者投诉。

2) 创建一个处置包装类,允许控制在处置包装器时是否实际处置其包装实例。例如(忽略 Dispose(bool) 噪音):

public class DispositionWrapper<T> : IDisposable
    where T : IDisposable
{
    private readonly T _instance;
    private bool _allowDisposition;

    public DispositionWrapper(T instance, bool allowDisposition)
    {
        if (instance == null)
        {
            throw new ArgumentNullException("instance");
        }

        this._instance = instance;
        this._allowDisposition = allowDisposition;
    }

    public T Instance
    {
        get
        {
            return this._instance;
        }
    }

    public void Dispose()
    {
        if (this._allowDisposition)
        {
            this._instance.Dispose();
        }
    }
}

3) 使用处置包装器来允许及早清理已知允许处置的实例。例如:

public class CustomGoRectangle : GoRectangle, IDisposable
{
    private DispositionWrapper<Pen> _ownedPen;

    public override Pen Pen
    {
        get
        {
            return this._ownedPen.Instance;
        }

        set
        {
            if (value == null)
            {
                this.OwnedPen = null;
            }
            else
            {
                this.OwnedPen = new DispositionWrapper<Pen>(value, false);
            }
        }
    }

    private DispositionWrapper<Pen> OwnedPen
    {
        get
        {
            return this._ownedPen;
        }

        set
        {
            if (this._ownedPen != null)
            {
                this._ownedPen.Dispose();
            }

            this._ownedPen = value;
        }
    }


    public void DoStuff()
    {
        this.OwnedPen = new DispositionWrapper<Pen>(new Pen(Color.Red, 4.0f), true);
    }

    public void DoOtherStuff()
    {
        this.OwnedPen = new DispositionWrapper<Pen>(new Pen(Color.Blue, 4.0f), true);
    }

    public void Test()
    {
        this.OwnedPen = new DispositionWrapper<Pen>(Pens.Green, false);
        this.DoStuff();
        this.DoOtherStuff();
    }

    public void Dispose()
    {
        if (this.OwnedPen != null)
        {
            this.OwnedPen.Dispose();
        }
    }
}

不幸的是,这意味着通过 Pen 属性分配的 Pen 实例在完成之前不会被清理。如果您担心这一点,您可能需要考虑扩展处置包装器,以检测是否可以通过反射读取其不可变字段的值来清理 Pen,如 jgauffin 的答案所述。

Here's the way I usually handle this sort of thing:

1) Complain to the library provider.

2) Create a disposition wrapper class that allows one to control whether its wrapped instance will actually be disposed when the wrapper is disposed. e.g. (ignoring Dispose(bool) noise):

public class DispositionWrapper<T> : IDisposable
    where T : IDisposable
{
    private readonly T _instance;
    private bool _allowDisposition;

    public DispositionWrapper(T instance, bool allowDisposition)
    {
        if (instance == null)
        {
            throw new ArgumentNullException("instance");
        }

        this._instance = instance;
        this._allowDisposition = allowDisposition;
    }

    public T Instance
    {
        get
        {
            return this._instance;
        }
    }

    public void Dispose()
    {
        if (this._allowDisposition)
        {
            this._instance.Dispose();
        }
    }
}

3) Use the disposition wrapper to allow early clean-up of instances for which disposition is known to be permissable. e.g.:

public class CustomGoRectangle : GoRectangle, IDisposable
{
    private DispositionWrapper<Pen> _ownedPen;

    public override Pen Pen
    {
        get
        {
            return this._ownedPen.Instance;
        }

        set
        {
            if (value == null)
            {
                this.OwnedPen = null;
            }
            else
            {
                this.OwnedPen = new DispositionWrapper<Pen>(value, false);
            }
        }
    }

    private DispositionWrapper<Pen> OwnedPen
    {
        get
        {
            return this._ownedPen;
        }

        set
        {
            if (this._ownedPen != null)
            {
                this._ownedPen.Dispose();
            }

            this._ownedPen = value;
        }
    }


    public void DoStuff()
    {
        this.OwnedPen = new DispositionWrapper<Pen>(new Pen(Color.Red, 4.0f), true);
    }

    public void DoOtherStuff()
    {
        this.OwnedPen = new DispositionWrapper<Pen>(new Pen(Color.Blue, 4.0f), true);
    }

    public void Test()
    {
        this.OwnedPen = new DispositionWrapper<Pen>(Pens.Green, false);
        this.DoStuff();
        this.DoOtherStuff();
    }

    public void Dispose()
    {
        if (this.OwnedPen != null)
        {
            this.OwnedPen.Dispose();
        }
    }
}

Unfortunately, this means that Pen instances assigned via the Pen property won't be cleaned up until they are finalized. If you are concerned about this, you might want to consider extending the disposition wrapper to detect whether the Pen can be cleaned up by reading the value of its immutable field via reflection, as mentioned jgauffin's answer.

极致的悲 2024-12-06 12:22:01

对于需要 IDisposable 对象来使用另一个对象的情况,理想的方法是使用一个参数来指示被赋予 IDisposable 对象引用的对象是否应该拥有该对象的所有权。坚持获取传入的 IDisposable 所有权的对象(例如 StreamReader)可能与从不接受所有权的对象一样烦人。鉴于 GoRectangle 不接受所有权,因此无论谁创建与 GoRectangle 一起使用的笔,都将负责处置它。假设应处置任何非不可变画笔的方法是危险的,因为无法保证被放弃的 GoRectangle 使用的任何画笔不会被仍在范围内的其他对象共享。

The ideal approach for the situation where an IDisposable object is needed to use another object is to have a parameter which indicates whether an object which is given a reference to an IDisposable object should take ownership of it. An object which insists upon taking ownership of a passed-in IDisposable (e.g. StreamReader) can be as annoying as one which never accepts ownership. Given that GoRectangle does not accept ownership, whoever creates a pen for use with a GoRectangle will be responsible for disposing it. Approaches which assume any non-immutable pen should be disposed are dangerous, because there's no guarantee that any pen that's used by a GoRectangle that's being abandoned won't be shared by some other object that's still in scope.

微凉 2024-12-06 12:22:01

任何 Disposable 对象都应该在不需要时手动处理,而不是依赖 GC。

在您的情况下,您可以采用 2 种策略:

  1. 在 CustomGoRectangle 外部创建 2 支笔(redPen 和 bluePen),或者使它们成为单独的成员,然后根据您的流程将其分配给 Pen 成员变量。
  2. 当你需要时创建一支笔来完成工作并处理掉它;并删除 Pen 成员变量。

Any Disposable object it should be disposed manually when no need it, and not relaying on GC.

In your case you can adopt 2 strategies:

  1. create 2 pens outside the CustomGoRectangle (redPen and bluePen) or make them separate members and just assign it to the Pen member variable based on your flow.
  2. create a pen when you need it do the work and dispose it than; and remove the Pen member variable.
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文