这个IDisposable的实现如何工作?

时间:2014-06-26 10:46:37

标签: c# asp.net .net entity-framework dispose

我即将构建我的第一个n层Web应用程序,因此,我在教育自己的原则,并查看示例代码。我在这里遇到了这种类型的应用程序的一个很好的例子:https://github.com/MarlabsInc/SocialGoal 但是,我对如何处理数据层上的EF上下文感到困惑。到目前为止,我已经处理了我的上下文,例如,通过使用using块,或者覆盖我的MVC控制器上的Dispose方法。但是,在此应用程序中,从不显式调用Dispose方法,而是实例化上下文的工厂类派生自实现IDisposable的基类:

工厂类:

public class DatabaseFactory : Disposable, IDatabaseFactory
{
    private SocialGoalEntities dataContext;
    public SocialGoalEntities Get()
    {
        return dataContext ?? (dataContext = new SocialGoalEntities());
    }
    protected override void DisposeCore()
    {
        if (dataContext != null)
            dataContext.Dispose();
    }
}

一次性课程:

public class Disposable : IDisposable
{
    private bool isDisposed;

    ~Disposable()
    {
        Dispose(false);
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }
    private void Dispose(bool disposing)
    {
        if (!isDisposed && disposing)
        {
            DisposeCore();
        }

        isDisposed = true;
    }

    protected virtual void DisposeCore()
    {
    }
}   

有了新手的眼睛,看起来上下文永远不会被处理掉。调用Dispose(bool)的唯一方法是终结器。由于这会将false作为参数传递,因此永远不会调用虚拟DisposeCore()方法,并且由于必须调用Context以进行处置,因此它永远不会被调用。我是否正确地思考这个问题,或者错过了一些关键的.NET知识?

2 个答案:

答案 0 :(得分:2)

此无参数Dispose()方法是IDisposable接口的经典实现,并且在Disposable基类上正确完成:

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

Dispose()将在任何using-block结束时调用,随后将调用Dispose(true)DisposeCore(),这可能会在派生类中实现以清理托管资源。

这不是一段特别好的代码

首先,这就是应该如何实施dispose-pattern:

public class MyDisposableThing : IDisposable
{
    public MyDisposableThing()
    {
        // Constructor
    }

    protected virtual void Dispose(bool disposing)
    {
        if (disposing)
        {
            // Dispose managed resources
        }

        // Dispose unmanaged resources
    }

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

    ~MyDisposableThing()
    {
        Dispose(false);
    }
}

可以通过两种方式销毁此类型的实例:

  1. 确定性:显式调用Dispose()或使用using-block结束
  2. 垃圾收集:实例由GC注定,并调用其终结器。
  3. 在第一种情况下,您希望发布托管资源 - 这就是您调用Dispose()的原因。在第二种情况下,您不必这样做,因为根据定义,它们是受管理的,GC应该像处理您自己的对象一样处理它们。您总是希望清理非托管资源。这就是disposing标志与相关if - 语句一起存在的原因。

    GC.SuppressFinalize()的调用是一种优化 - 它告诉垃圾收集器该实例已被Dispose()清除,并且无需再次调用其终结符~MyDisposableThing()。 / p>

    最后,请注意Dispose(bool disposing)virtual,这非常重要。引入需要在处理期间处理的新资源的派生类现在可以覆盖它,并且在释放自己的资源(托管或非托管)之后,他们可以调用base.Dispose(disposing)

    public class MyDerivedDisposableThing : MyDisposableThing
    {
        public MyDerivedDisposableThing()
        {
            // Constructor
        }
    
        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                // Dispose managed resources
            }
    
            // Dispose unmanaged resources
    
            // Call base class
            base.Dispose(disposing);
        }
    }
    

    将此与您的代码段对比

    首先,DisposeCore()没有参数,也不会从终结器调用(因为disposing将为false),因此阻止派生类利用整个模式并清理非托管资源。

    其次,isDisposed是私有的,这使得它在基类上完全没有必要和无用,因为基类非常原始。

    <强>意见

    根本不要创建基础Disposable类。只需正确实现Dispose模式,如本答案以及MSDN

    所示

    http://msdn.microsoft.com/en-us/library/fs2xkftw(v=vs.110).aspx

    这有点不合适,但最好做得恰到好处。

答案 1 :(得分:0)

垃圾收集器无论如何都要清理或释放您的托管对象/资源。仅当您急需释放这些资源(例如图像)时,才需要为托管对象或资源进行处置。如果框架没有在工厂调用Dispose方法,那么只有您可以决定何时调用该Dispose方法(可能在某些事件上,例如,关闭一个特定的选项卡。)。那么你确定,你不再需要这些资源,处理它们会释放内存。