最近我看到一些写成以下形式的代码:
public void Dipose()
{
using(_myDisposableField) { }
}
对我来说,这似乎很奇怪,我更喜欢看到myDisposableField.Dispose();
使用“using”释放对象的原因是什么,明确调用与之相比有哪些好处?
最近我看到一些写成以下形式的代码:
public void Dipose()
{
using(_myDisposableField) { }
}
对我来说,这似乎很奇怪,我更喜欢看到myDisposableField.Dispose();
使用“using”释放对象的原因是什么,明确调用与之相比有哪些好处?
try/finally
块,并最终调用Dispose
方法。
移除它。这将使代码更快、更易读,也可能更重要的是(随着下面的阅读),更能表达其意图。
更新:他们稍微聪明了一点,等效的代码需要进行空值检查,并且根据Jon Skeet的建议,在多线程的情况下也要进行本地拷贝(与标准事件调用模式相同,以避免在空值检查和方法调用之间发生竞态条件)。IDisposable tmp = _myDisposableField;
if (tmp != null)
tmp.Dispose();
根据我在编写示例应用程序的IL中看到的内容,似乎你还需要将_myDisposableField
直接视为IDisposable
。如果任何类型同时提供public void Dispose()
方法并显式实现IDisposable
接口,那么这点非常重要。
这段代码也没有尝试复制使用using
时存在的try-finally
,但可以认为这是不必要的。然而,正如Michael Graczyk在评论中指出的那样,使用finally
可以提供保护来处理异常,特别是ThreadAbortException
(可能在任何时候发生)。尽管如此,实际发生这种情况的机会非常小。
虽然我打赌他们这样做并没有真正理解它给他们带来了什么微妙的“好处”。
IDisposable
也很重要,因为隐式和显式接口定义存在差异。 - Adam Houldsworth如此答案所讨论的,这是避免空测试的聪明方式,但是:它可能不仅仅是这样。在现代C#中,在许多情况下,您可以使用空条件运算符实现类似的效果:
public void Dipose()
=> _myDisposableField?.Dispose();
然而,_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) {}
}
A : IDisposable { public void Dispose(); void IDisposable.Dispose(); }
和 B : A, IDisposable { public new void Dispose(); void IDisposable.Dispose(); }
:) - Marc Gravell你贴出的示例中存在一个非常微妙但危险的bug。
虽然它编译成:
try {}
finally
{
if (_myDisposableField != null)
((IDisposable)_myDisposableField).Dispose();
}
对象应该在using语句内部实例化,而不是外部:
你可以实例化资源对象并将变量传递给using语句,但这不是最佳实践。在这种情况下,在控制离开using块之后,对象仍然在作用域内,但可能无法访问其非托管资源。换句话说,它不再完全初始化。如果你尝试在using块外部使用对象,则有可能引发异常。因此,最好在using语句中实例化对象并将其作用域限制为using块。
换句话说,这是一种肮脏的方法。
干净的版本在MSDN上非常清楚地说明:
using
块。不要直接使用Dispose
。最后,请注意,只有在使用非托管资源时才应该使用IDisposable
模式。确保真正需要使用它 :-)
using语句定义了引用对象应在其后的代码段完成后被释放的范围。
是的,你可以在结束时调用.dispose,但这会让对象的作用域变得不太清晰(依我的看法)。因人而异。
using
的原因并不重要。作用域是类成员,使用 using
不会使任何内容更清晰。调用 Dispose
实际上会使它更清晰,因为 using
倾向于用于在同一作用域中声明、使用和处理的内容。 - Adam Houldsworth