在Dispose中清空“using”语句

时间:2012-08-02 13:45:56

标签: c#

最近我看到一些代码编写如下:

public void Dipose()
{
   using(_myDisposableField) { }
}

这对我来说似乎很奇怪,我更愿意看到myDisposableField.Dispose();

使用“使用”处理您的对象而不是明确地进行调用有什么原因?

4 个答案:

答案 0 :(得分:23)

不,根本没有。它只会编译成空try/finally并最终调用Dispose

删除它。您可以使代码更快,更易读,也许最重要的是(在下面继续阅读)更多 表达意图

更新:他们略微聪明,等效代码需要空检查,根据Jon Skeet的建议,如果涉及多线程,也需要本地副本(以与标准事件调用模式相同的方式避免在null检查和方法调用之间发生争用。)

IDisposable tmp = _myDisposableField; 

if (tmp != null) 
    tmp.Dispose();

从我在我编写的示例应用程序的IL中可以看到,看起来您还需要直接将_myDisposableField视为IDisposable。如果任何类型明确实现IDisposable接口 并且 同时提供public void Dispose()方法,这将非常重要。

此代码也不会尝试复制使用try-finally时存在的using,但有人认为这被认为是不必要的。正如Michael Graczyk在评论中指出的那样,finally的使用提供了针对异常的保护,特别是ThreadAbortException(可能在任何时候发生)。也就是说,实际发生这种情况的窗口非常小。

尽管如此,我认为他们确实没有真正理解它给予他们的微妙“好处”这一事实。

答案 1 :(得分:2)

你发布的例子中有一个非常微妙但又邪恶的错误。

虽然它“编译”到:

try {}
finally
{
    if (_myDisposableField != null) 
        ((IDisposable)_myDisposableField).Dispose();
}

对象应该在using子句中实例化,而不是在外部:

  

您可以实例化资源对象,然后将变量传递给using语句,但是这不是最佳做法。在这种情况下,在控件离开using块之后,对象仍然在范围内虽然它可能不再能够访问其非托管资源。换句话说,它将不再完全初始化。如果您尝试使用using块之外的对象,则可能会导致抛出异常。因此,通常最好在using语句中实例化对象,并将其范围限制为使用块。

- using Statement (C# Reference)

换句话说,它很肮脏和黑客。

在MSDN上非常清楚地说明了干净的版本:

  • 如果您可以将实例的使用限制为方法,则在其边框上使用带有构造函数调用的using块。请勿直接使用Dispose
  • 如果您需要(但确实需要)保持实例处于活动状态直到父级被释放,则使用Disposable pattern显式处置,而不是其他任何内容。实现一个dispose级联有不同的方法,但是它们需要完全相同,以避免非常微妙和难以捕获错误。 Framework Design Guidelines上有一个非常好的MSDN资源。

最后,请注意以下内容:如果您使用非托管资源,则只应使用IDisposable模式。确保它真的需要: - )

答案 2 :(得分:1)

正如已经讨论过的in this answer一样,这是一种避免空值测试的厚脸皮方法,但是:除此以外,还有更多的选择。在现代C#中,在许多情况下,您可以使用空条件运算符实现类似的目标:

public void Dipose()
    => _myDisposableField?.Dispose();

但是,{API _myDisposableField的类型不需要在公共API上具有Dispose();可能是:

public class Foo : IDisposable {
    void IDisposable.Dispose() {...}
}

或更糟糕的是:

public class Bar : IDisposable {
    void IDisposable.Dispose() {...}
    public void Dispose() {...} // some completely different meaning! DO NOT DO THIS!
}

在第一种情况下,Dispose()将找不到方法,在第二种情况下,Dispose()将调用错误的方法。在这两种情况下,using技巧都将起作用,强制类型转换也将起作用(尽管如果是值类型,这将再次略有不同):

public void Dipose()
    => ((IDisposable)_myDisposableField)?.Dispose();

如果不确定类型是否可抛弃(在某些多态情况下会发生这种情况),也可以使用以下任一方法:

public void Dipose()
    => (_myDisposableField as IDisposable)?.Dispose();

或:

public void Dipose()
{
    using (_myDisposableField as IDisposable) {}
}

答案 3 :(得分:-2)

using语句定义了应该处理引用对象的代码范围。

是的,你可以在完成后调用.dispose,但不太清楚(恕我直言)对象的范围是什么。 YMMV。