最近我看到一些代码写成如下:
public void Dipose()
{
using(_myDisposableField) { }
}
这对我来说似乎很奇怪,我更愿意看到myDisposableField.Dispose();
使用“使用”来处理对象而不是显式调用的原因是什么?
最近我看到一些代码写成如下:
public void Dipose()
{
using(_myDisposableField) { }
}
这对我来说似乎很奇怪,我更愿意看到myDisposableField.Dispose();
使用“使用”来处理对象而不是显式调用的原因是什么?
不,根本没有。它只会编译成一个空try/finally
并最终调用Dispose
.
去掉它。您将使代码更快、更易读,也许最重要的是(当您在下面继续阅读时)在其意图上更具表现力。
更新:他们有点聪明,等效代码需要一个空值检查,并且根据 Jon Skeet 的建议,如果涉及多线程,也需要一个本地副本(与标准事件调用模式相同,以避免空值之间的竞争检查和方法调用)。
IDisposable tmp = _myDisposableField;
if (tmp != null)
tmp.Dispose();
从我在我编写的示例应用程序的 IL 中可以看到,您似乎还需要直接_myDisposableField
对待IDisposable
。如果任何类型显IDisposable
式实现接口并同时提供方法,这将很重要。public void Dispose()
此代码也不会尝试复制try-finally
使用 时存在的using
,但假设这是不必要的。然而,正如 Michael Graczyk 在评论中指出的那样,使用finally
提供了对异常的保护,特别是ThreadAbortException
(可能在任何时候发生)。也就是说,实际发生这种情况的窗口非常小。
虽然,我会在他们这样做并没有真正理解它给他们带来的微妙“好处”这一事实上赌一把。
您发布的示例中有一个非常微妙但邪恶的错误。
虽然它“编译”为:
try {}
finally
{
if (_myDisposableField != null)
((IDisposable)_myDisposableField).Dispose();
}
对象应该在 using 子句中实例化,而不是在外部:
您可以实例化资源对象,然后将变量传递给 using 语句,但这不是最佳实践。在这种情况下,在控制离开 using 块后,对象仍然在范围内,但可能无法访问其非托管资源。换句话说,它不再完全初始化。如果您尝试在 using 块之外使用该对象,则可能会引发异常。出于这个原因,最好在 using 语句中实例化对象并将其范围限制在 using 块中。
—<a href="https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/using-statement" rel="nofollow noreferrer">using 语句(C# 参考)
换句话说,它是肮脏而骇人的。
干净的版本在 MSDN 上有非常清楚的说明:
using
在其边框上带有构造函数调用的块。不要Dispose
直接使用。最后,请注意以下内容,如果您使用非托管资源,您应该只使用该IDisposable
模式。确保它真的需要:-)
正如在这个答案中已经讨论过的,这是避免空测试的一种厚颜无耻的方式,但是:它可能不止于此。在现代 C# 中,在许多情况下,您可以使用空条件运算符实现类似的效果:
public void Dipose()
=> _myDisposableField?.Dispose();
但是,公共 API 上不需要 (of _myDisposableField
)类型;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) {}
}
using 语句定义了应在其后释放引用对象的代码范围。
是的,您可以在完成后调用 .dispose ,但不太清楚(恕我直言)对象的范围是什么。YMMV。