22

最近我看到一些代码写成如下:

public void Dipose()
{
   using(_myDisposableField) { }
}

这对我来说似乎很奇怪,我更愿意看到myDisposableField.Dispose();

使用“使用”来处理对象而不是显式调用的原因是什么?

4

4 回答 4

24

不,根本没有。它只会编译成一个空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(可能在任何时候发生)。也就是说,实际发生这种情况的窗口非常小。

虽然,我会在他们这样做并没有真正理解它给他们带来的微妙“好处”这一事实上赌一把。

于 2012-08-02T13:48:10.113 回答
2

您发布的示例中有一个非常微妙但邪恶的错误。

虽然它“编译”为:

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直接使用。
  • 如果您需要(但确实需要)使实例保持活动状态,直到父级被处置,然后使用Disposable 模式显式处置,仅此而已。有不同的方式来实现 dispose 级联,但是它们都需要类似地完成以避免非常微妙和难以捕捉的错误。框架设计指南中的 MSDN 上有一个非常好的资源。

最后,请注意以下内容,如果您使用非托管资源,您应该只使用该IDisposable模式。确保它真的需要:-)

于 2012-10-15T22:24:39.173 回答
1

正如在这个答案中已经讨论过的,这是避免空测试的一种厚颜无耻的方式,但是:它可能不止于此。在现代 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) {}
}
于 2020-09-09T08:40:11.027 回答
-2

using 语句定义了应在其后释放引用对象的代码范围。

是的,您可以在完成后调用 .dispose ,但不太清楚(恕我直言)对象的范围是什么。YMMV。

于 2012-08-02T13:51:44.073 回答