首页 文章

我应该Dispose()DataSet和DataTable吗?

提问于
浏览
180

DataSet和DataTable都实现了IDisposable,因此,通过传统的最佳实践,我应该调用它们的Dispose()方法 .

但是,从我到目前为止所读到的,DataSet和DataTable实际上并没有任何非托管资源,因此Dispose()实际上并没有做太多 .

另外,我不能只使用 using(DataSet myDataSet...) ,因为DataSet有一组DataTables .

所以,为了安全起见,我需要遍历myDataSet.Tables,处理每个DataTables,然后处理DataSet .

那么,在我的所有DataSet和DataTable上调用Dispose()是否值得麻烦?

Addendum:

对于那些认为应该处理DataSet的人:通常,处理的模式是使用 usingtry..finally ,因为您要保证将调用Dispose() .

然而,这对于一个集合来说真的很快 . 例如,如果对Dispose()的一个调用抛出异常,你会怎么做?你吞下它(这是“坏”),以便你可以继续处理下一个元素?

或者,你是否建议我只调用myDataSet.Dispose(),而忘记在myDataSet.Tables中处理DataTables?

11 回答

  • 132

    我会在对象实现IDisposeable时调用dispose . 这是有原因的 .

    DataSet可能是巨大的内存生长 . 他们越早被标记为清理就越好 .

    update

    我回答这个问题已经5年了 . 我仍然同意我的回答 . 如果存在dispose方法,则在完成对象时应调用它 . IDispose接口的实现是有原因的 .

  • 1

    即使对象没有非托管资源,处置也可能通过破坏对象图来帮助GC . 通常,如果object实现了IDisposable,则应调用Dispose() .

    Dispose()实际上是否做某事取决于给定的类 . 在DataSet的情况下,Dispose()实现继承自MarshalByValueComponent . 它从容器中删除自己并调用Disposed事件 . 源代码如下(用.NET Reflector反汇编):

    protected virtual void Dispose(bool disposing)
    {
        if (disposing)
        {
            lock (this)
            {
                if ((this.site != null) && (this.site.Container != null))
                {
                    this.site.Container.Remove(this);
                }
                if (this.events != null)
                {
                    EventHandler handler = (EventHandler) this.events[EventDisposed];
                    if (handler != null)
                    {
                        handler(this, EventArgs.Empty);
                    }
                }
            }
        }
    }
    
  • 22

    数据集实现了IDisposable彻底的MarshalByValueComponent,它实现了IDisposable . 由于管理数据集,因此调用dispose没有任何实际好处 .

  • 16

    你自己创建DataTables吗?因为通常不需要遍历任何Object的子节点(如在DataSet.Tables中),因为它是Parent的工作来处置它的所有子成员 .

    通常,规则是:如果您创建了它并且它实现了IDisposable,请将其公开 . 如果你没有创建它,那么不要处置它,这是父对象的工作 . 但是每个对象可能都有特殊规则,请查看文档 .

    对于.net 3.5,它明确地说“不再使用时处理它”,这就是我要做的 .

  • -1

    首先,我将检查Dispose对DataSet的作用 . 也许使用redgate的反射器会有所帮助 .

  • 0

    不需要Dispose()因为DataSet继承了MarshalByValueComponent类,而MarshalByValueComponent实现了IDisposable接口

  • 6

    以下是一些讨论,解释了为什么Dispose不是DataSet所必需的 .

    To Dispose or Not to Dispose ?

    DataSet中的Dispose方法仅存在因为继承的副作用 - 换句话说,它实际上并没有在最终化中做任何有用的事情 .

    Should Dispose be called on DataTable and DataSet objects?包括MVP的一些解释:

    system.data命名空间(ADONET)不包含非托管资源 . 因此,只要您没有添加一些特殊的东西,就没有必要处置它们 .

    Understanding the Dispose method and datasets?与当局Scott Allen发表评论:

    在实践中,我们很少处理DataSet,因为它提供的好处不大“

    所以,有共识的是 there is currently no good reason to call Dispose on a DataSet.

  • 4

    您应该假设它执行了一些有用的操作并调用Dispose,即使它当前没有任何操作 . NET Framework的版本,不能保证它将在未来的版本中保持这种方式导致资源使用效率低下 .

  • 120

    Update (December 1, 2009):

    我想修改这个答案,并承认原来的答案是有缺陷的 .

    原始分析 does 适用于需要最终确定的对象 - 并且如果没有准确,深入的理解,表面上不应接受的做法仍然存在 .

    然而,事实证明,DataSets,DataViews,DataTables suppress finalization in their constructors - 这就是为什么在它们上面调用Dispose()显然什么都不做 .

    据推测,这是因为他们没有非托管资源;因此,尽管 MarshalByValueComponent 允许非托管资源,但这些特定实现并不需要,因此可以放弃最终确定 .

    (.NET作者会注意抑制通常占用最多内存的类型的最终化,这说明了这种实践对于可终结类型的重要性 . )

    尽管如此,自.NET Framework(大约8年前)开始以来,这些细节仍未得到充分记录 . 非常令人惊讶的是(你基本上只能依靠自己的设备来筛选,但是将这些碎片放在一起的冲突,模糊的材料有时令人沮丧,但确实提供了对我们日常依赖的框架的更完整的理解) .

    经过大量阅读,这是我的理解:

    如果一个对象需要最终化,那么 could 占用的内存比它需要的时间长 - 这就是原因:a)任何定义析构函数的类型(或者从定义析构函数的类型继承)都被认为是可终结的; b)在分配时(在构造函数运行之前),指针放在Finalization队列上; c)可终结物体通常需要回收 2 collections (而不是标准1); d)禁止终结不会从终结队列中删除对象(由SOS中的!FinalizeQueue报告)此命令具有误导性;知道最终化队列中的对象(在其本身中)是没有用的;知道最终化队列中的对象并且仍然需要最终确定将是有帮助的(是否有一个命令?)

    抑制终结会在对象的标头中稍稍关闭,向运行时指示它不需要调用其Finalizer(不需要移动FReachable队列);它保留在Finalization队列中(并继续由SOS中的!FinalizeQueue报告)

    DataTable,DataSet,DataView类都以MarshalByValueComponent为根,MarshalByValueComponent是一个可以(可能)处理非托管资源的可终结对象

    • 因为DataTable,DataSet,DataView不引入非托管资源,所以它们在构造函数中抑制了最终化

    • 虽然这是一种不寻常的模式,但它使调用者不必担心在使用后调用Dispose

    • 这可能是DataTables可能在不同的DataSet之间共享的事实,这可能是DataSets不关心处理子DataTables的原因 .

    • 这也意味着这些对象将出现在SOS中的!FinalizeQueue下

    • However, these objects should still be reclaimable after a single collection, like their non-finalizable counterparts

    4 (new references):

    Original Answer:

    在这方面有很多误导性和一般性很差的答案 - 任何落在这里的人都应该忽略噪音并仔细阅读下面的参考文献 .

    毫无疑问,Dispose should be 调用任何Finalizable对象 .

    DataTables are Finalizable .

    调用Dispose significantly 可加快回收内存的速度 .

    MarshalByValueComponent 在其Dispose()中调用 GC.SuppressFinalize(this) - 跳过这意味着在回收内存之前必须等待数十个(如果不是数百个)Gen0集合:

    通过对最终化的基本理解,我们已经可以推导出一些非常重要的事情:首先,需要最终化的对象比没有完成的对象长寿 . 事实上,他们可以活得更久 . 例如,假设gen2中的对象需要最终确定 . 最终化将被安排,但对象仍然在gen2中,因此在下一代gen2集合发生之前不会重新收集 . 这确实可能是一段很长的时间,事实上,如果事情进展顺利,那将是一段很长的时间,因为gen2集合成本很高,因此我们希望它们很少发生 . 需要完成的旧对象可能必须等待几十个(如果不是数百个)gen0集合才能回收它们的空间 . 其次,需要最终确定的物体会造成附带损害 . 由于内部对象指针必须保持有效,因此不仅直接需要最终化的对象会在内存中停留,而且对象引用的所有内容也将直接或间接地保留在内存中 . 如果一个巨大的对象树被一个需要最终化的对象锚定,那么整个树就会徘徊,这可能是我们刚才讨论的很长一段时间 . 因此,谨慎使用终结器并将它们放在具有尽可能少的内部对象指针的对象上是很重要的 . 在我刚给出的树例子中,您可以通过将需要最终化的资源移动到单独的对象并在树的根中保持对该对象的引用来轻松避免该问题 . 通过适度的更改,只有一个对象(希望是一个漂亮的小对象)会停留在一起并且最终化成本最小化 . 最后,需要完成的对象为终结器线程创建工作 . 如果你的终结过程很复杂,那么唯一的终结者线程会花费大量时间来执行这些步骤,这会导致工作积压,从而导致更多对象等待完成 . 因此,终结者尽可能少地工作是至关重要的 . 还要记住,虽然所有对象指针在最终确定期间仍然有效,但可能是这些指针导致已经完成的对象,因此可能不太有用 . 通常最安全的是避免在终结代码中跟踪对象指针,即使指针是有效 . 安全,简短的终结代码路径是最好的 .

    从Gen2中看到100多个未引用的DataTables MB的人那里得到它:这非常重要,完全错过了这个帖子的答案 .

    References:

    1 - http://msdn.microsoft.com/en-us/library/ms973837.aspx

    2 - http://vineetgupta.spaces.live.com/blog/cns!8DE4BDC896BEE1AD!1104.entry http://www.dotnetfunda.com/articles/article524-net-best-practice-no-2-improve-garbage-collector-performance-using-finalizedispose-pattern.aspx

    3 - http://codeidol.com/csharp/net-framework/Inside-the-CLR/Automatic-Memory-Management/

  • 7

    如果您的意图或此问题的上下文实际上是垃圾收集,那么您可以将数据集和数据表明确设置为null或使用关键字using并让它们超出范围 . 尽管Tetraneutron早些时候说过,但处理并不多 . GC将收集不再引用的数据集对象以及超出范围的数据集对象 .

    我真的希望在强制要求回答之前,强迫人们投票实际写评论 .

  • 0

    尝试使用Clear()函数 . 这对我来说非常适合处置 .

    DataTable dt = GetDataSchema();
    //populate dt, do whatever...
    dt.Clear();
    

相关问题