C#中使用Dataset时的"using"关键字

3

在C#中,我对IDisposable接口和using关键字有些困惑,不知道哪些类可以使用它们。

using (DataSet studentDS = GetMyStudentDS())
{
    // here comes some code
}

我发现继承自DataSet类的类不会被垃圾回收器终止,如果这样的话,将其放入using块中以便在完成任务后手动完成是否是一种好的做法。有人能为大家简要说明一下吗?谢谢。


可能是重复问题,参考链接:https://dev59.com/H3RC5IYBdhLWcg3wYP6h - Likurg
澄清一下,using语句并不会强制垃圾回收器运行终结器。using语句仅仅保证IDisposable对象的Dispose()方法会被执行。 - trickdev
1
数据集是一个特殊情况:https://dev59.com/XnNA5IYBdhLWcg3wjOve “DataSet中的Dispose方法仅存在于继承的副作用--换句话说,在最终化中它实际上并没有做任何有用的事情。” - HugoRune
6个回答

4

把它放在using块中是一个好的做法吗?

是的。因为DataSet实现了IDisposible接口。DataSet扩展了MarshalByValueComponent类。MarshalByValueComponent类实现了IDisposable。

任何派生类型也将实现IDisposable。如果它没有显式实现(换句话说,覆盖IDisposable.Dispose),那么将调用基方法(DataSet.Dispose)。

关于Finalizer,这是一个单独的概念,以确保即使开发人员未调用Dispose方法(或忘记使用块),也会清理非托管资源。


话虽如此,但对于数据集,使用Using块是一个好的实践。这是否意味着垃圾回收周期会在稍后的某个时间清除内存缓存中的数据集,而使用块可能会在其工作完成后立即执行清除操作? - user824910
1
Dispose和Garbage collection是不同但相关的概念。Dispose通常用于释放垃圾收集器没有作用的非托管资源。垃圾收集器只处理托管内存。数据集内部使用一些非托管资源,因此它实现了Dispose。如果您没有指定使用块或忘记调用Dispose,则非托管资源将不会被释放。技巧(带有性能惩罚)是在终结器中调用Dispose - Tilak
对于你的问题,如果一个类实现了Dispose,使用using块是一个好习惯。 - Tilak

4

如果我在数据集中处理了大量的数据,手动强制进行垃圾回收是否有好处呢?虽然CLR会在某个时候自动调用垃圾回收器。 - user824910
IDisposable并不是GC的替代品。“using”语句不会进行垃圾回收,因此您不必担心“谁来负责垃圾回收”。 - Mert Akcakaya
@Mert 我知道。使用语句会处理对象并释放MarshalByValueComponent使用的所有资源,正如MSDN所述。我是在说,如果他想让CLR管理内存,我会让它自己处理。如果OP想要立即进行垃圾回收,可以使用GC.Collect(); - Darren

2

析构函数和IDisposable模式是有区别的。析构函数在非确定性时间由GC调用,用于清理与类相关的非托管资源并释放实例占用的内存。析构函数运行的时间是无法控制的。

IDisposable用于在确定性时间清理对象。它不会释放对象占用的内存,但通常用于关闭文件、数据库连接等操作。

因此,一般规则是,如果一个对象实现了IDisposable接口,最好使用using关键字包装其实例,以便尽快释放资源。


0

DataSets 实现了 IDisposable 接口,但是它们抑制了其终结器。
因此,实际上不需要使用终结器,因为它们不会引入任何非托管代码。
不要使用 using 语句包装它,但是在使用 SQL 连接或数据读取器时不要忘记使用 using 关键字。


0

C# 允许您在实现 IDisposable 的所有内容上使用 using。为什么?因为 using 只是一种语法糖。 using(obj){/*somecode*/} 实际上就像这样:

try
{
    /*somecode*/
}
finally
{
    if(obj!=null) obj.Dispose();
}

如果您正在使用Typed DataSet,您可能已经注意到您的自动生成的类没有覆盖Dispose(bool)。因此,Dispose对于您的自动生成的表和列无效。
为了帮助数据集释放内存(它确实存在问题),请在自动生成的TypedDataSet(在部分文件中)中覆盖Dispose(bool)。可以像这样做:
protected override void Dispose(bool disposing)
{
    foreach (DataTable tab in this.Tables)
    {
        if (tab != null)
        {
            tab.Clear();
            tab.Columns.Clear();
            tab.Dispose();
        }
    }
    this.Tables.Clear();
    base.Dispose(disposing);
}

现在在这个 SataSet 上使用 using 将会很有意义。


-1
是的,你绝对可以在 using 块中使用任何对象初始化代码。使用它不会有任何副作用。无论在什么情况下,如果你想要百分之百地确保一个对象被垃圾回收,请将其放在 using 块中。

垃圾回收和调用Dispose根本不是同一回事。调用Dispose(这就是using语句所做的)并不会导致对象被垃圾回收。 - Jon Skeet

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