我应该 Dispose() DataSet 和 DataTable 吗?

发布于 2024-11-15 06:11:12 字数 685 浏览 3 评论 0 原文

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

然而,从我到目前为止所读到的内容来看,DataSet 和 DataTable 实际上没有任何非托管资源,因此 Dispose() 实际上并没有做太多事情。

另外,我不能只使用 using(DataSet myDataSet...) 因为 DataSet 有一个 DataTable 集合。

因此,为了安全起见,我需要遍历 myDataSet.Tables,处理每个 DataTable,然后处理 DataSet。

那么,是否值得在我的所有数据集和数据表上调用 Dispose() 呢?

附录:

对于那些认为应该处置 DataSet 的人: 一般来说,处理的模式是使用 usingtry..finally,因为你想保证 Dispose() 会被调用。

然而,对于一个集合来说,这很快就会变得丑陋。例如,如果对 Dispose() 的调用之一引发异常,您该怎么办?你是否吞下它(这是“坏的”),以便你可以继续处理下一个元素?

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

DataSet and DataTable both implement IDisposable, so, by conventional best practices, I should call their Dispose() methods.

However, from what I've read so far, DataSet and DataTable don't actually have any unmanaged resources, so Dispose() doesn't actually do much.

Plus, I can't just use using(DataSet myDataSet...) because DataSet has a collection of DataTables.

So, to be safe, I'd need to iterate through myDataSet.Tables, dispose of each of the DataTables, then dispose of the DataSet.

So, is it worth the hassle to call Dispose() on all of my DataSets and DataTables?

Addendum:

For those of you who think that DataSet should be disposed:
In general, the pattern for disposing is to use using or try..finally, because you want to guarantee that Dispose() will be called.

However, this gets ugly real fast for a collection. For example, what do you do if one of the calls to Dispose() thrown an exception? Do you swallow it (which is "bad") so that you can continue on to dispose the next element?

Or, do you suggest that I just call myDataSet.Dispose(), and forget about disposing the DataTables in myDataSet.Tables?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(12

陌上青苔 2024-11-22 06:11:12

以下是一些讨论,解释了为什么 DataSet 不需要 Dispose。

处置还是不处置?

DataSet 中的 Dispose 方法的存在只是因为继承的副作用——换句话说,它实际上在终结过程中没有做任何有用的事情。

应该对 DataTable 和 DataSet 对象调用 Dispose 吗? 包括来自 MVP 的一些解释:

system.data 命名空间 (ADONET) 不包含
非托管资源。因此没有必要将其中任何一个作为
只要你没有给自己添加一些特别的东西。

了解 Dispose 方法和数据集?有来自权威 Scott Allen 的评论:

在实践中,我们很少处理数据集,因为它几乎没有什么好处”

因此,大家一致认为,目前没有充分的理由在 DataSet 上调用 Dispose。

Here are a couple of discussions explaining why Dispose is not necessary for a DataSet.

To Dispose or Not to Dispose ?:

The Dispose method in DataSet exists ONLY because of side effect of inheritance-- in other words, it doesn't actually do anything useful in the finalization.

Should Dispose be called on DataTable and DataSet objects? includes some explanation from an MVP:

The system.data namespace (ADONET) does not contain
unmanaged resources. Therefore there is no need to dispose any of those as
long as you have not added yourself something special to it.

Understanding the Dispose method and datasets? has a with comment from authority Scott Allen:

In pratice we rarely Dispose a DataSet because it offers little benefit"

So, the consensus there is that there is currently no good reason to call Dispose on a DataSet.

﹏半生如梦愿梦如真 2024-11-22 06:11:12

更新(2009 年 12 月 1 日):

我想修改此答案并承认原始答案有缺陷。

最初的分析确实适用于需要最终确定的对象,并且在没有准确、深入理解的情况下不应接受表面实践的观点仍然成立。

然而,事实证明,DataSet、DataView、DataTable抑制其构造函数中的终结——这就是为什么对它们显式调用 Dispose() 不会执行任何操作。

据推测,发生这种情况是因为他们没有非托管资源;因此,尽管 MarshalByValueComponent 允许非托管资源,但这些特定的实现没有必要,因此可以放弃最终确定。

(.NET 作者会小心地抑制通常占用最多内存的类型的终结,这说明了这种做法对于可终结类型的重要性。)

尽管如此,自 .NET 诞生以来,这些细节仍然没有得到充分记录 。 .NET Framework(大约 8 年前)非常令人惊讶(您基本上需要依靠自己的设备来筛选相互冲突、模棱两可的材料,然后将各个部分组合在一起,这有时会令人沮丧,但确实提供了对我们每天依赖的框架)。

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

如果一个对象需要终结,它可能占用内存的时间比它需要的时间更长 - 原因如下:a) 定义析构函数的任何类型(或从定义析构函数的类型继承)被认为是可终结的; b) 在分配时(在构造函数运行之前),一个指针被放置在 Finalization 队列上; c) 可终结的对象通常需要回收2个集合(而不是标准的1个); d) 抑制终结不会从终结队列中删除对象(如 SOS 中的 !FinalizeQueue 所报告)
该命令具有误导性;知道终结队列上有哪些对象(就其本身而言)并没有什么帮助;知道哪些对象位于终结队列上并且仍然需要终结会很有帮助(是否有一个命令?)

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

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

  • 因为 DataTable、DataSet、DataView 不不会引入非托管资源,它们会抑制构造函数中的终结
  • 虽然这是一种不寻常的模式,但它使调用者不必担心在使用后调用 Dispose
  • ,而且 DataTables 的事实可能会在不同的 DataSet 之间共享,这可能是 DataSet 不关心处理子 DataTable 的原因
  • 这也意味着这些对象将出现在 SOS 中的 !FinalizeQueue 下
  • 但是,这些对象在单次收集后仍然应该是可回收的,就像它们的不可终结的对应物

4(新引用):

原始答案:

有很多关于这个问题的误导性且通常非常糟糕的答案 - 任何来到这里的人都应该忽略噪音并仔细阅读下面的参考资料。

毫无疑问,应该对任何可终结的对象调用 Dispose。

数据表可最终确定的。

调用 Dispose 显着加快内存回收速度。

MarshalByValueComponent 在其 Dispose() 中调用 GC.SuppressFinalize(this) - 跳过此操作意味着在回收内存之前必须等待数十个甚至数百个 Gen0 集合:

有了对终结的基本了解,我们
已经可以推导出一些非常重要的
事情:

首先,需要终结的对象
比不存在的物体寿命更长。
事实上,他们可以活得更久。
例如,假设一个对象
是在第二代中需要最终确定。
最终确定将按计划进行,但
对象仍在 gen2 中,所以它会
直到下一次才重新收集
发生第 2 代收集。那可能是
确实很长一段时间,而且事实上,
如果一切顺利的话,这将是
很长时间了,因为 gen2 合集
成本高昂,因此我们希望他们
很少发生。年长的
需要终结的对象可能
如果没有就得等几十个
之前有数百个 gen0 收藏
他们的空间被回收。

二、需要finalization的对象
造成附带损害。自从
内部对象指针必须保留
有效,不仅是对象
直接需要最终化延迟
在内存中,但一切都是对象
直接和间接指的是,
也将留在记忆中。如果一个巨大的
对象树由一个锚定
需要的单个对象
最终化,然后是整个树
会徘徊,可能会持续很长时间
我们刚才讨论的时间。这是
因此使用终结器很重要
谨慎地将它们放置在物体上
具有尽可能少的内部对象
尽可能指点。在树上
我刚刚给出的例子,你可以轻松地
通过移动来避免该问题
需要最终确定的资源
单独的对象并保留
引用根中的该对象
树的。有了这个小小的改变
只有一个对象(希望是一个好的
小物体)会徘徊并且
最终确定成本最小化。

最后,需要终结的对象
为终结器线程创建工作。
如果您的最终确定过程是
复杂的一,唯一的
终结器线程将花费
花很多时间执行这些步骤,
这可能会导致工作积压
因此导致更多物体停留
等待最终确定。所以,
至关重要的是
终结器所做的工作与
可能的。还要记住,虽然
所有对象指针仍然有效
在最终确定期间,可能是
这些指针导致的情况
已经存在的对象
最终确定,因此可能会减少
比有用。通常最安全的是
避免跟随对象指针
完成代码即使
指针有效。安全、短
最终确定代码路径是最好的。

从在 Gen2 中看到过数百 MB 的非引用数据表的人那里得到的信息:这非常重要,并且完全被该线程的答案所忽略。

参考文献:

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

<一个href="http://www.dotnetfunda.com/articles/article524-net-best-practice-no-2-improve-garbage-collector-performance-using-finalizedispose-pattern.aspx" rel="noreferrer">3 -
http://codeidol.com/csharp/net-框架/CLR内部/自动内存管理/

Update (December 1, 2009):

I'd like to amend this answer and concede that the original answer was flawed.

The original analysis does apply to objects that require finalization – and the point that practices shouldn’t be accepted on the surface without an accurate, in-depth understanding still stands.

However, it turns out that DataSets, DataViews, DataTables suppress finalization in their constructors – this is why calling Dispose() on them explicitly does nothing.

Presumably, this happens because they don’t have unmanaged resources; so despite the fact that MarshalByValueComponent makes allowances for unmanaged resources, these particular implementations don’t have the need and can therefore forgo finalization.

(That .NET authors would take care to suppress finalization on the very types that normally occupy the most memory speaks to the importance of this practice in general for finalizable types.)

Notwithstanding, that these details are still under-documented since the inception of the .NET Framework (almost 8 years ago) is pretty surprising (that you’re essentially left to your own devices to sift though conflicting, ambiguous material to put the pieces together is frustrating at times but does provide a more complete understanding of the framework we rely on everyday).

After lots of reading, here’s my understanding:

If an object requires finalization, it could occupy memory longer than it needs to – here’s why: a) Any type that defines a destructor (or inherits from a type that defines a destructor) is considered finalizable; b) On allocation (before the constructor runs), a pointer is placed on the Finalization queue; c) A finalizable object normally requires 2 collections to be reclaimed (instead of the standard 1); d) Suppressing finalization doesn’t remove an object from the finalization queue (as reported by !FinalizeQueue in SOS)
This command is misleading; Knowing what objects are on the finalization queue (in and of itself) isn’t helpful; Knowing what objects are on the finalization queue and still require finalization would be helpful (is there a command for this?)

Suppressing finalization turns a bit off in the object's header indicating to the runtime that it doesn’t need to have its Finalizer invoked (doesn’t need to move the FReachable queue); It remains on the Finalization queue (and continues to be reported by !FinalizeQueue in SOS)

The DataTable, DataSet, DataView classes are all rooted at MarshalByValueComponent, a finalizable object that can (potentially) handle unmanaged resources

  • Because DataTable, DataSet, DataView don’t introduce unmanaged resources, they suppress finalization in their constructors
  • While this is an unusual pattern, it frees the caller from having to worry about calling Dispose after use
  • This, and the fact that DataTables can potentially be shared across different DataSets, is likely why DataSets don’t care to dispose child DataTables
  • This also means that these objects will appear under the !FinalizeQueue in SOS
  • However, these objects should still be reclaimable after a single collection, like their non-finalizable counterparts

4 (new references):

Original Answer:

There are a lot of misleading and generally very poor answers on this - anyone who's landed here should ignore the noise and read the references below carefully.

Without a doubt, Dispose should be called on any Finalizable objects.

DataTables are Finalizable.

Calling Dispose significantly speeds up the reclaiming of memory.

MarshalByValueComponent calls GC.SuppressFinalize(this) in its Dispose() - skipping this means having to wait for dozens if not hundreds of Gen0 collections before memory is reclaimed:

With this basic understanding of finalization we
can already deduce some very important
things:

First, objects that need finalization
live longer than objects that do not.
In fact, they can live a lot longer.
For instance, suppose an object that
is in gen2 needs to be finalized.
Finalization will be scheduled but the
object is still in gen2, so it will
not be re-collected until the next
gen2 collection happens. That could be
a very long time indeed, and, in fact,
if things are going well it will be a
long time, because gen2 collections
are costly and thus we want them to
happen very infrequently. Older
objects needing finalization might
have to wait for dozens if not
hundreds of gen0 collections before
their space is reclaimed.

Second, objects that need finalization
cause collateral damage. Since the
internal object pointers must remain
valid, not only will the objects
directly needing finalization linger
in memory but everything the object
refers to, directly and indirectly,
will also remain in memory. If a huge
tree of objects was anchored by a
single object that required
finalization, then the entire tree
would linger, potentially for a long
time as we just discussed. It is
therefore important to use finalizers
sparingly and place them on objects
that have as few internal object
pointers as possible. In the tree
example I just gave, you can easily
avoid the problem by moving the
resources in need of finalization to a
separate object and keeping a
reference to that object in the root
of the tree. With that modest change
only the one object (hopefully a nice
small object) would linger and the
finalization cost is minimized.

Finally, objects needing finalization
create work for the finalizer thread.
If your finalization process is a
complex one, the one and only
finalizer thread will be spending a
lot of time performing those steps,
which can cause a backlog of work and
therefore cause more objects to linger
waiting for finalization. Therefore,
it is vitally important that
finalizers do as little work as
possible. Remember also that although
all object pointers remain valid
during finalization, it might be the
case that those pointers lead to
objects that have already been
finalized and might therefore be less
than useful. It is generally safest to
avoid following object pointers in
finalization code even though the
pointers are valid. A safe, short
finalization code path is the best.

Take it from someone who's seen 100s of MBs of non-referenced DataTables in Gen2: this is hugely important and completely missed by the answers on this thread.

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/

棒棒糖 2024-11-22 06:11:12

您应该假设它做了一些有用的事情并调用 Dispose,即使它在当前的 .NET Framework 版本中什么也不做。无法保证它在未来版本中会保持这种状态,从而导致资源使用效率低下。

You should assume it does something useful and call Dispose even if it does nothing in current .NET Framework incarnations. There's no guarantee it will stay that way in future versions leading to inefficient resource usage.

澉约 2024-11-22 06:11:12

即使对象没有非托管资源,处置也可能通过破坏对象图来帮助 GC。一般来说,如果一个对象实现了 IDisposable,就应该调用 Dispose()。

Dispose() 是否实际执行某些操作取决于给定的类。对于 DataSet,Dispose() 实现继承自 MarshalByValueComponent。它将自身从容器中删除并调用 Dispose 事件。源代码如下(用.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);
                }
            }
        }
    }
}

Even if an object has no unmanaged resources, disposing might help GC by breaking object graphs. In general, if an object implements IDisposable, Dispose() should be called.

Whether Dispose() actually does something or not depends on the given class. In case of DataSet, Dispose() implementation is inherited from MarshalByValueComponent. It removes itself from container and calls Disposed event. The source code is below (disassembled with .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);
                }
            }
        }
    }
}
尐籹人 2024-11-22 06:11:12

您自己创建数据表吗?因为通常不需要迭代任何对象的子对象(如在 DataSet.Tables 中),因为父对象的工作是处理其所有子成员。

一般来说,规则是:如果您创建了它并且它实现了 IDisposable,则将其释放。如果您没有创建它,则不要处置它,这是父对象的工作。但每个对象可能有特殊的规则,请查看文档。

对于.NET 3.5,它明确表示“不再使用时将其丢弃”,所以这就是我要做的。

Do you create the DataTables yourself? Because iterating through the children of any Object (as in DataSet.Tables) is usually not needed, as it's the job of the Parent to dispose all its child members.

Generally, the rule is: If you created it and it implements IDisposable, Dispose it. If you did NOT create it, then do NOT dispose it, that's the job of the parent object. But each object may have special rules, check the Documentation.

For .NET 3.5, it explicitly says "Dispose it when not using anymore", so that's what I would do.

丶视觉 2024-11-22 06:11:12

每当对象实现 IDisposeable 时,我都会调用 dispose。它的存在是有原因的。

数据集可能会占用大量内存。越早标记为清理越好。

更新

我回答这个问题已经五年了。我还是同意我的回答。如果有一个 dispose 方法,那么当你处理完该对象时应该调用它。实现 IDispose 接口是有原因的。

I call dispose anytime an object implements IDisposeable. It's there for a reason.

DataSets can be huge memory hogs. The sooner they can be marked for clean up, the better.

update

It's been 5 years since I answered this question. I still agree with my answer. If there is a dispose method, it should be called when you are done with the object. The IDispose interface was implemented for a reason.

若相惜即相离 2024-11-22 06:11:12

如果您的意图或这个问题的上下文确实是垃圾收集,那么您可以显式将数据集和数据表设置为 null 或使用关键字 using 并让它们超出范围。 Dispose 并没有像 Tetraneutron 之前所说的那样起多大作用。 GC 将收集不再引用的数据集对象以及超出范围的数据集对象。

我真的希望迫使人们在对答案投反对票之前先写下评论。

If your intention or the context of this question is really garbage collection, then you can set the datasets and datatables to null explicitly or use the keyword using and let them go out of scope. Dispose does not do much as Tetraneutron said it earlier. GC will collect dataset objects that are no longer referenced and also those that are out of scope.

I really wish SO forced people down voting to actually write a comment before downvoting the answer.

青瓷清茶倾城歌 2024-11-22 06:11:12

数据集通过 MarshalByValueComponent 实现 IDisposable,MarshalByValueComponent 实现了 IDisposable。由于数据集是受管理的,因此调用 dispose 并没有真正的好处。

Datasets implement IDisposable thorough MarshalByValueComponent, which implements IDisposable. Since datasets are managed there is no real benefit to calling dispose.

忘你却要生生世世 2024-11-22 06:11:12

尝试使用 Clear() 函数。
它对我来说非常适合处理。

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

Try to use Clear() function.
It works great for me for disposing.

DataTable dt = GetDataSchema();
//populate dt, do whatever...
dt.Clear();
記柔刀 2024-11-22 06:11:12

无需Dispose()
因为DataSet继承了MarshalByValueComponent类,并且MarshalByValueComponent实现了IDisposable接口

No need to Dispose()
because DataSet inherit MarshalByValueComponent class and MarshalByValueComponent implement IDisposable Interface

红墙和绿瓦 2024-11-22 06:11:12

这是正确处理DataTable的正确方法。

private DataTable CreateSchema_Table()
{
    DataTable td = null;
    try
    {
        td = new DataTable();
        //use table DataTable here
        
        return td.Copy();
    }
    catch {  }
    finally
    {
        if (td != null)
        {
            td.Constraints.Clear();
            td.Clear();
            td.Dispose();
            td = null;
        }
    }
}

This is the right way to properly Dispose the DataTable.

private DataTable CreateSchema_Table()
{
    DataTable td = null;
    try
    {
        td = new DataTable();
        //use table DataTable here
        
        return td.Copy();
    }
    catch {  }
    finally
    {
        if (td != null)
        {
            td.Constraints.Clear();
            td.Clear();
            td.Dispose();
            td = null;
        }
    }
}
笑梦风尘 2024-11-22 06:11:12

这可能是处理和释放 DataSet 消耗的内存的最佳/正确方法。

try
    {
        DataSet ds = new DataSet("DS");
        //use table DataTable here
        
    }
    catch {  }
    finally
    {
        if (ds != null)
                {
                    ds.EnforceConstraints = false;
                    ds.Relations.Clear();
                    int totalCount = ds.Tables.Count;

                    for (int i = totalCount - 1; i >= 0; i--)
                    {
                        DataTable td1 = ds.Tables[i];
                        if (td1 != null)
                        {
                            td1.Constraints.Clear();
                            td1.Clear();
                            td1.Dispose();
                            td1 = null;
                        }
                    }

                    ds.Tables.Clear();
                    ds.Dispose();
                    ds = null;
                }
    }

And this can be the best/proper way to Dispose and release the memory consumed by DataSet.

try
    {
        DataSet ds = new DataSet("DS");
        //use table DataTable here
        
    }
    catch {  }
    finally
    {
        if (ds != null)
                {
                    ds.EnforceConstraints = false;
                    ds.Relations.Clear();
                    int totalCount = ds.Tables.Count;

                    for (int i = totalCount - 1; i >= 0; i--)
                    {
                        DataTable td1 = ds.Tables[i];
                        if (td1 != null)
                        {
                            td1.Constraints.Clear();
                            td1.Clear();
                            td1.Dispose();
                            td1 = null;
                        }
                    }

                    ds.Tables.Clear();
                    ds.Dispose();
                    ds = null;
                }
    }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文