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();
}