设置IDisposable属性时调用Dispose?

时间:2011-08-15 14:12:31

标签: c# idisposable

我今天改变了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位于第三方库中,并且具有Pen属性,它不是IDisposable。

我把笔放在很多地方,比如上面的人为例子。这里说明的问题是调用DoStuff()我创建了一个新的笔,它从未被处理过。现在我可以调用dispose.Pen如果在分配新值之前它不是null,但是当Test()说明系统笔是否已经设置时,这将导致进一步的问题。处理这种情况的实际模式是什么?

5 个答案:

答案 0 :(得分:2)

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

答案 1 :(得分:1)

不,你不能丢弃所有的笔,因为如果你试图处理它们,系统笔会抛出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();
}

在分配新笔之前调用它。

答案 2 :(得分:1)

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

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)使用处置包装器允许及早清理已知允许处置的实例。 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();
        }
    }
}

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

答案 3 :(得分:1)

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

答案 4 :(得分:0)

任何一次性物品,不需要时应手动处理,不在GC上转发。

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

  1. 在CustomGoRectangle(redPen和bluePen)之外创建2个笔或使它们成为单独的成员,并根据您的流程将其分配给Pen成员变量。
  2. 在你需要它的时候创造一支笔并且处理它比;并删除Pen成员变量。