处理IDisposable对象的通用函数

时间:2010-07-06 20:12:51

标签: c# generics idisposable

我正在处理一个处理很多Sql对象的类 - Connection,Command,DataAdapter,CommandBuilder等。有多个实例我们有这样的代码:

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

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

etc

我知道这在重复方面相当不足,但它已经开始闻起来了。我认为它闻起来的原因是因为在某些情况下对象也被设置为null。

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

如果可能,我希望摆脱重复。我已经提出了这种通用方法来处理一个对象并将其设置为null。

private void DisposeObject<TDisposable>( ref TDisposable disposableObject )
    where TDisposable : class, IDisposable
{
    if( disposableObject != null )
    {
        disposableObject.Dispose();
        disposableObject = null;
    }
}

我的问题是......

  1. 这个通用功能是个坏主意吗?
  2. 是否有必要将对象设置为null
  3. 编辑:

    我知道using语句,但我不能总是使用它,因为我有一些成员变量需要持续超过一次调用。例如,连接和事务对象。

    谢谢!

10 个答案:

答案 0 :(得分:7)

您应该考虑是否可以使用using声明。

using (SqlCommand command = ...)
{
    // ...
}

这可确保在控件离开使用范围时在命令对象上调用Dispose。与您自己编写清理代码相比,这有许多优点:

  • 它更简洁。
  • 变量永远不会设置为null - 它在一个语句中声明和初始化。
  • 当对象被处置时,变量超出范围,因此您可以降低意外尝试访问已处置资源的风险。
  • 这是例外安全。
  • 如果使用语句进行嵌套,则资源会按正确的顺序自然处理(与创建它们的顺序相反)。
  

是否有必要将对象设置为null?

完成使用后,通常不需要将变量设置为null。重要的是,在使用完资源后调用Dispose。如果使用上述模式,则不仅不必将变量设置为null - 它将产生编译错误:

Cannot assign to 'c' because it is a 'using variable'

需要注意的一点是using仅在获取对象并在同一方法调用中处理时才有效。如果您的资源需要在多个方法调用期间保持活动状态,则无法使用此模式。在这种情况下,您可能希望使类实现IDisposable并确保在调用Dispose方法时清除资源。在这种情况下,您将需要像您所写的代码。在这种情况下,将变量设置为null并没有错,但这并不重要,因为垃圾收集器无论如何都会正确清理内存。重要的是确保在调用dispose方法时处置您拥有的所有资源,并且您正在这样做。

一些实施细节:

  • 您应该确保如果您的Dispose被调用两次,它不会抛出异常。您的实用程序功能正确处理此情况。
  • 如果您的对象已被处置,您应确保对象上的相关方法引发ObjectDisposedException

答案 1 :(得分:6)

您应该在拥有这些字段的类中实现IDisposable。有关此主题,请参阅my blog post。如果这不能很好地工作,那么该类不遵循OOP原则,需要重构。

在处置后将变量设置为nullnot necessary

答案 2 :(得分:3)

如果您的对象需要进行大量清理,他们可能希望在单独的一次性用品列表中跟踪需要删除的内容,并立即处理所有这些内容。然后在拆解时它不需要记住需要处理的所有东西(也不需要检查空值,它只是在列表中查找)。

这可能不会构建,但出于扩展目的,您可以在类中包含RecycleBin。那么班级只需要处理垃圾箱。

public class RecycleBin : IDisposable
{
    private List<IDisposable> _toDispose = new List<IDisposable>();

    public void RememberToDispose(IDisposable disposable)
    {
        _toDispose.Add(disposable);
    }

    public void Dispose()
    {
        foreach(var d in _toDispose)
            d.Dispose();

        _toDispose.Clear();
    }
}

答案 3 :(得分:1)

我认为这些是字段而不是局部变量,因此using关键字没有意义。

  

这个通用功能是个坏主意吗?

我认为这是一个好主意,而且我已经使用了几次相似的功能; +1使其成为通用。

  

是否有必要将对象设置为null?

从技术上讲,对象应允许多次调用其Dispose方法。 (例如,如果在最终确定期间对象复活,则会发生这种情况。)实际上,您是否信任这些类的作者或是否要进行防御性编码取决于您。就个人而言,我检查null,然后将引用设置为null。

编辑:如果此代码位于您自己的对象的Dispose方法中,则无法设置对null的引用将不会泄漏内存。相反,它可以方便地防止双重处置。

答案 4 :(得分:1)

我将假设您正在使用一种方法创建资源,将其置于另一种方法中,并在一个或多个其他方法中使用它,使using语句对您无用。

在这种情况下,你的方法非常好。

到目前为止,问题的第二部分(“将其设置为null必要吗?”),简单的答案是“不,但它不会伤害任何东西”。

大多数对象拥有一个资源 - 内存,垃圾收集处理释放,因此我们不必担心它。有些还拥有一些其他资源:文件句柄,数据库连接等。对于第二类,我们必须实现IDisposable,以释放其他资源。

一旦调用Dispose方法,两个类别都因为相同:它们持有内存。在这种情况下,我们可以让变量超出范围,删除对内存的引用,并允许GC最终释放它 - 或者我们可以强制解决问题,通过将变量设置为null,并显式删除参考记忆。我们仍然需要等到GC开始实际释放内存,并且很可能该变量无论如何都会超出范围,之后将其设置为null,因此在绝大多数情况下,它将具有根本没有效果,但在少数几种情况下,它会让内存在几秒前被释放。

但是,如果您在特定情况下检查是否应该调用Dispose,那么您可能 将其设置为null,如果您有可能调用Dispose ()两次。

答案 5 :(得分:1)

鉴于iDisposable不包含任何确定对象是否已被处置的标准方法,我喜欢在处理它们时将其设置为null。可以肯定的是,处理已经处理过的物体是无害的,但是能够在观察窗口中检查物体并且一眼就能看出哪些区域已被处置是很好的。能够进行代码测试以确保应该处理的对象也是很好的(假设代码遵循在处理它们时将变量置零的惯例,并且在任何时候都没有)。

答案 6 :(得分:0)

为什么不使用C#构造? http://msdn.microsoft.com/en-us/library/yh598w02.aspx 如果不需要,设置为null。

答案 7 :(得分:0)

其他人推荐了using构造,我也建议这样做。但是,我想指出的是,即使你确实需要一种实用方法,也完全没有必要以你已经完成的方式使它成为通用的。只需声明您的方法即可获得IDisposable

private static void DisposeObject( ref IDisposable disposableObject )
{
    if( disposableObject != null )
    {
        disposableObject.Dispose();
        disposableObject = null;
    }
}

答案 8 :(得分:0)

您永远不需要将变量设置为nullIDisposable.Dispose的重点是让对象进入一种状态,它可以无害地在内存中无处不在,直到GC完成它,所以你只需“丢弃并忘记”。

我很好奇为什么你认为你不能使用using声明。在一个方法中创建一个对象并以不同的方法处理它是我书中的 真的 大代码气味,因为你很快就会忘记在哪里开放。最好像这样重构代码:

using(var xxx = whatever()) {
    LotsOfProcessing(xxx);
    EvenMoreProcessing(xxx);
    NowUseItAgain(xxx);
}

我确信这有一个标准的模式名称,但我只是称之为“摧毁你创造的一切,但没有别的”。

答案 9 :(得分:0)

我在问题中回答了这个问题并在此回答:

Implementing IDisposable (the Disposable Pattern) as a service (class member)

它实现了一个简单的,可重用的组件,适用于任何IDisposable成员。