C# - 我知道,我知道,另一个关于Dispose的问题(与设计更相关)!

时间:2010-01-06 15:32:58

标签: c# dispose

  

可能重复:
  Should I Dispose() DataSet and DataTable?

     

OP评论:我想说“我应该Dispose()DataSet和DataTable吗?”链接不是一个可能的解决方案。这是一个很好的链接,但这与设计有关。相反,忽略exposed属性是一个DataSet,并用应该处理的东西替换它。同样的问题也适用于那里。

我正在搞乱Crystal Reports,我有一个“ReportData”类。换句话说,这个类封装了我将使用的DataSet的“填充”。

public class ReportData
{
    private DataSet1 m_DS = null; // Notice this disposable member variable

    public ReportData( ... some parameters ...)
    {
        m_DS = new DataSet1();
        // Plus some other manipulation on m_DS
    }

    public DataSet1 GetDataSet
    {
        get
        {
            return m_DS;
        }
    }

    // Everything else is pretty much private.
    // This class is here to generate my DataSet
}

以下是其他一些课程的使用方法:

private void SetReportDataSource()
{
    DataSet1 ds = m_RptData.GetDataSet;
    m_Rpt.SetDataSource(ds);
}

我几乎在学习C#(在一本介绍书中阅读几章,然后继续阅读,一路上搜索所有内容)。据我所知,如果它实现了IDisposable,你最好配置它。 DataSet实现了IDisposable,因此我们需要Dispose它。

这是设计部分的用武之地:

问题1a:我是否将ReportData类设为IDisposable?

换句话说,看起来我可以这样做并完成它:

private void SetReportDataSource()
{
    using (DataSet1 ds = m_RptData.GetDataSet)
    {
        m_Rpt.SetDataSource(ds);
    }
}
问题1b:我是否应该以某种方式更具防御性?

我不知道,我想我真的,真的想确保它被处理掉。例如,以我的SetReportDatsSource函数为例。我使用“使用”,但其他人可能会使用该类而忘记添加使用或以某种方式调用Dispose。因此,我转到我的ReportData类:

public class ReportData : IDisposable
{
    private DataSet1 m_DS = null; // Notice this disposable member variable
    private bool m_IsDisposed = false; // This is new!

    public ReportData( ... some parameters ...)
    {
        m_DS = new DataSet1();
        // Plus some other manipulation on m_DS
    }

    // New code here (up until the GetDataSet property)
    ~ReportData()
    {
        Dispose(false);
    }

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

    protected virtual void Dispose(bool disposing)
    {
        if (m_IsDisposed == true)
        {
            return;
        }

        if (m_DS != null)
        {
            m_DS.Dispose();
            m_DS = null;
        }

        m_IsDisposed = true;
    }

    // Done with new code

    public DataSet1 GetDataSet
    {
        get
        {
            return m_DS;
        }
    }

    // Everything else is pretty much private.
    // This class is here to generate my DataSet
}

现在,让我们回到调用类,我们仍然有:

private void SetReportDataSource()
{
    using (DataSet1 ds = m_RptData.GetDataSet)
    {
        m_Rpt.SetDataSource(ds);
    }
}

但是,我现在也将ReportData(m_RptData)丢弃了!所以,我们要去处理它!因为它是一个成员变量(我不能像我在SetReportDataSource中使用它那样使用“使用”),所以你开始考虑使这个调用类具有IDisposable,所以我可以:

    protected virtual void Dispose(bool disposing)
    {
        if (m_IsDisposed == true)
        {
            return;
        }

        if (m_ReportData != null)
        {
            m_ReportData.Dispose();
            m_ReportData = null;
        }

        m_IsDisposed = true;
    }

所以,现在这个类有析构函数/终结符及其公共Dispose方法,处理ReportData。我们保证我们处理DataSet!

但话说再次,这将导致DataSet的Dispose方法被调用两次。因为SetReportDataSource函数处理公开的DataSet,而ReportData类也处理相同的事情(并且没有一种简单的方法来确定是否有人处理了公开的DataSet)。

似乎有点疯狂。在我看来,我:

a)可能会过度思考这个(或者只是想要真正的防守,这很好)!

b)可能会跳过一堆篮球。

也许规则应该是:如果我的类要暴露它,那么调用函数应该负责处理它。

我看到的唯一问题(这就是我在这里发布的原因):

在实例化ReportData成员变量和调用SetReportDataSource之间,可能会发生一些错误/异常。所以,我们永远不会有机会在我们的DataSet上进行“使用”(在SetReportDataSource内部)。但是该数据集是使用ReportData构建的(我们想在其上调用dispose!)

所以,现在我们回到制作ReportData IDisposable,因为我们至少需要一些公共的“CleanUp”功能......好吧,我已经完成了。 :)

4 个答案:

答案 0 :(得分:4)

如果您持有实现IDisposable的东西,最简单的模式是自己实现IDisposable,并在您拥有的所有IDisposable上调用Dispose方法。更加防守而不是看起来不可行;如果有人使用你的班级忘记来调用你的Dispose方法,那你怎么知道他们是用你做的?

编辑:再想一想,Finalize调用会告诉您,您的客户已经完成了您的使用;他们没有更多的提及你...

答案 1 :(得分:3)

请参阅:Should I Dispose() DataSet and DataTable?

如上所述,如果一个对象实现了IDisposable,那么你应该调用Dispose()。 [在.NET框架中有一些地方原始设计师,可能是他们需要Dispose(),但后来不需要它。但最安全和正确的做法是无论如何都要调用Dispose。]

答案 2 :(得分:1)

您没有显示SetDataSource的代码,但是这段代码只有在SetDataSource立即使用DataSet的内容并且不存储对它的引用时才会起作用:

private void SetReportDataSource()
{
    using (DataSet1 ds = m_RptData.GetDataSet)
    {
        m_Rpt.SetDataSource(ds);
    }
}

至于你的其余代码和问题(引用已删除的答案):


您没有任何需要最终确定的内容。您可能已经注意到,当您发现您没有使用处置参数时。所有你需要的是:

void Dispose() {
  if (m_DS != null) m_DS.Dispose()
}  

终结者的一般规则是:如果有疑问,请不要 直接使用非托管资源时,您只需要一个。

答案 3 :(得分:0)

答案1A:我会推荐使用语法,原因是它似乎是处理这种性质的标准。

答案1B:如果其他人正在使用您的课程,我认为您的意思是延伸,实施者可以处理他认为合适的任何更改。根据Dispose()上的文档

  

如果多次调用对象的Dispose方法,则该对象必须忽略第一个之后的所有调用。如果多次调用Dispose方法,则该对象不得抛出异常。如果发生错误,Dispose可以抛出异常,因为资源已经被释放,并且之前没有调用Dispose。 (http://msdn.microsoft.com/en-us/library/system.idisposable.dispose%28VS.71%29.aspx

垃圾收集器会在某个时刻抓住您的对象并处理它。除非发生灾难。