C# “空的”;使用;处置中的声明
最近我看到一些代码编写如下:C# “空的”;使用;处置中的声明,c#,C#,最近我看到一些代码编写如下: public void Dipose() { using(_myDisposableField) { } } 这对我来说似乎很奇怪,我更喜欢看myDisposableField.Dispose() 使用“using”来处理对象而不是显式地进行调用有什么原因?没有,根本没有。它将编译成一个空的try/finally,并最终调用Dispose 移除它。您将使代码更快、更具可读性,也许最重要的是(当您继续阅读下面的内容时)在意图上更具表达力 更新:他们有点聪明,等
public void Dipose()
{
using(_myDisposableField) { }
}
这对我来说似乎很奇怪,我更喜欢看myDisposableField.Dispose()代码>
使用“using”来处理对象而不是显式地进行调用有什么原因?没有,根本没有。它将编译成一个空的try/finally
,并最终调用Dispose
移除它。您将使代码更快、更具可读性,也许最重要的是(当您继续阅读下面的内容时)在意图上更具表达力
更新:他们有点聪明,等效代码需要空检查,根据Jon Skeet的建议,如果涉及多线程,也需要本地副本(与标准事件调用模式相同,以避免空检查和方法调用之间的竞争)
从我编写的示例应用程序的IL中可以看到,您似乎还需要直接将\u myDisposableField
视为IDisposable
。如果任何类型显式地实现了IDisposable
接口,并且同时提供了public void Dispose()
方法,则这一点非常重要
此代码也不会尝试复制使用
时存在的try finally
,但在某种程度上认为这是不必要的。然而,正如Michael Graczyk在评论中指出的那样,finally
的使用提供了针对异常的保护,特别是ThreadAbortException
(可能在任何时候发生)。这就是说,实际发生这种情况的窗口非常小
尽管如此,我敢打赌,他们这样做并没有真正理解它给他们带来了什么微妙的“好处”。using语句定义了应该处理引用对象的代码范围
是的,您可以在完成后调用.dispose,但对象的范围不太清楚(IMHO)。YMMV.在您发布的示例中有一个非常微妙但邪恶的bug 虽然它“编译”到: 对象应在using子句内实例化,而不是在以下外部实例化: 您可以实例化资源对象,然后将变量传递给using语句,,但这不是最佳做法。在这种情况下,控件离开using块后,对象仍在作用域中,但可能无法访问其非托管资源。换句话说,它不再完全初始化。如果尝试在using块之外使用该对象,则可能导致引发异常。因此,最好在using语句中实例化对象,并将其范围限制在using块中 - 换句话说,它又脏又黑 清洁版在MSDN上有非常清楚的说明:
- 如果您可以将实例的使用限制为一个方法,那么使用
块,并在其边界上使用构造函数调用。不要直接使用using
Dispose
- 如果您需要(但确实需要)在释放父实例之前保持实例的活动状态,则可以使用而不使用其他方法显式地进行释放。实现dispose级联有不同的方法,但是它们都需要以类似的方式完成,以避免非常微妙和难以捕获的bug。在MSDN中有一个非常好的资源
IDisposable
模式。确保确实需要它:-)正如前面所讨论的,这是避免空测试的一种厚颜无耻的方法,但是:它可以有更多的内容。在现代C#中,在许多情况下,使用空条件运算符可以实现类似的效果:
public void Dipose()
=>_myDisposableField?.Dispose();
但是,不要求类型(myDisposableField的
)在公共API上具有Dispose()
;它可以是:
公共类Foo:IDisposable{
void IDisposable.Dispose(){…}
}
甚至更糟的是:
公共类栏:IDisposable{
void IDisposable.Dispose(){…}
public void Dispose(){…}//有些完全不同的含义!不要这样做!
}
在第一种情况下,Dispose()
将无法找到该方法,而在第二种情况下,Dispose()
将调用错误的方法。在上述任何一种情况下,使用技巧的都会起作用,演员阵容也会起作用(尽管如果是值类型的话,效果会略有不同):
public void Dipose()
=>((IDisposable)\u myDisposableField)?.Dispose();
如果您不确定该类型是否为一次性类型(在某些多态性场景中会发生这种情况),您还可以使用:
public void Dipose()
=>(_myDisposableField为IDisposable)?.Dispose();
或:
public void Dipose()
{
使用(_myDisposableField作为IDisposable){}
}
看起来作者认为他/她很聪明,但我看不出有任何理由这样做……要么是缺少一些代码,要么是有人需要更彻底地审查他们的代码,直到他们学会如何正确编写。在我看来,没有理由投反对票,因为结果非常有趣。@JonSkeet True。但是再一次,在这两种情况下,共同点是不加评论地责骂负责这项工作的开发人员。我不知道如何解析这一点。你是{因为他们这样做}而不加评论,还是因为{因为他们这样做而不加评论}而给他们打耳光?:)@乔恩,他们两个都应该挨两巴掌。正手和反手:-P@JonSkeet由于隐式和显式接口定义的不同,将变量视为IDisposable
也很重要。@AdamHouldsworth还有一个重要的区别。OP发布的代码将运行\u myDisposableField.IDis
IDisposable tmp = _myDisposableField;
if (tmp != null)
tmp.Dispose();
try {}
finally
{
if (_myDisposableField != null)
((IDisposable)_myDisposableField).Dispose();
}