处理实现了IDisposable接口的嵌套对象

4

我在我的项目中有以下代码。我需要显式地释放内部类吗?如果需要,应该在哪里释放?

public class Outer : IDisposable
{
    Context context = new Context();
    Inner inner;

    public Outer()
    {
        inner = new Inner(context);
    }

    public void Dispose()
    {
        context.Dispose();
    }
}

public class Inner : IDisposable
{
    Context context;

    public Inner(Context context)
    {
        this.context = context;
    }

    public void Dispose()
    {
        context.Dispose();
    }
}

上下文是类似于Entity Framework中的DbContext。

5个回答

7

在这种情况下,您需要确定应该实际“拥有”上下文的内容。如果您已经在Inner中拥有它,那么您是否真的需要在Outer中也拥有它呢?它们两个中哪一个真正负责它们?在我看来,您真正想要的是:

public sealed class Outer : IDisposable
{
    private readonly Inner inner;

    public Outer()
    {
        inner = new Inner(new Context());
    }

    public void Dispose()
    {
        inner.Dispose();
    }
}

public sealed class Inner : IDisposable
{
    private readonly Context context;

    public Inner(Context context)
    {
        this.context = context;
    }

    public void Dispose()
    {
        context.Dispose();
    }
}

请注意,将OuterInner都设为sealed后,就不需要编写protected Dispose(bool disposing)等方法了- 这实际上是为继承而设计的,而继承经常会带来麻烦。如果您确实需要通过子类化OuterInner来释放更多资源,则需要更复杂的实现。
个人建议尽可能避免实现IDisposable,并将可释放的对象仅限于使用using语句的本地变量中。当然,这并不总是可行的,但值得一试...

2

是的,最好将 inner 销毁,因为它实现了 IDisposable 接口并由 Outer 拥有。

事实上,在这种特定的设置中,它被证明是多余的并不重要。Inner 可能会在另一种情况下使用,或者自己发生变化。您应该将您的实现与此隔离开来。

如果可以的话,您可能需要重新考虑 Inner 是否应该销毁上下文。它没有创建上下文,而是通过传递获得它。如果能消除 Inner.context 更好。


但是如果外部和内部都被处理了会发生什么?它们都试图处理相同的资源。 - Ucodia
2
@Ucodia,没问题。Dispose()的契约是它应该安全地被多次调用。如果有疑问,最好过于频繁地调用它,而不是冒着完全不调用它的风险。 - H H

0

是的,但您还应正确实现IDisposable。

  private bool _disposed;

  public void Dispose()
  {
     Dispose(true);
     GC.SuppressFinalize(this);
  }

  protected virtual void Dispose(bool disposing)
  {
     if (!_disposed)
     {
        if (disposing)
        {
           // Managed
           inner.Dispose();
        }

        // Unmanaged
     }

     _disposed = true;
  }

  ~Outer()
  {
     Dispose(false);
  } 

4
这里没有迹象表明真的需要使用终结器,或者使用“完整”模式。个人认为应该将Outer和Inner都密封起来,这时只需要一个简单的Dispose方法即可,不需要更复杂的处理。 - Jon Skeet
“只是调用dispose的终结器”是不是毫无意义(甚至有害)? 终结器是用于处理本机资源的最后一道防线。包含本机资源的类应实现终结器和IDisposable。仅包含其他IDisposable对象的对象不需要终结器,它们可以依赖内部对象的终结器。 - cdleonard
我的答案源于规则CA1063 链接。但我同意,如果你只封闭类型,那么就不需要完整的模式。 - harlam357

0
在这种特定情况下,你其实不需要这么做,因为你已经处理了上下文。
然而,无论如何你都应该处理它,以防Inner.Dispose发生变化。
总的来说,你应该倾向于处理事物。
对同一个对象进行两次处理不会造成问题。

0

根据粘贴的代码,应该由外部类Dispose上下文。如果您可以将分配和处理保持在一起,那就太好了。外部类可以非常明智地管理上下文对象的生命周期。

释放您可以访问的所有对象是一个坏主意。您只应该处理自己分配的那些对象。

在内部类中处理也可能意味着在处理完内部对象后无法使用外部对象。这在您发布的示例中很好,但通常不是这样。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接