最近我看到一些代码编写如下:
public void Dipose()
{
using(_myDisposableField) { }
}
这对我来说似乎很奇怪,我更愿意看到myDisposableField.Dispose();
使用“使用”处理您的对象而不是明确地进行调用有什么原因?
答案 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
。最后,请注意以下内容:如果您使用非托管资源,则只应使用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。