Using statement vs. IDisposable.Dispose()

本文关键字:Dispose IDisposable statement vs Using | 更新日期: 2023-09-27 18:36:53

据我了解,一旦代码退出块,.NET 中的 using 语句就会调用IDisposable对象的Dispose()方法。

using声明还有其他作用吗?如果不是,则以下两个代码示例似乎实现了完全相同的功能:

Using Con as New Connection()
    Con.Open()
    'do whatever '
End Using
Dim Con as New Connection()
Con.Open()
'do whatever '
Con.Dispose()

我会给任何确认我是正确的人或指出我错了并解释原因的人最好的答案。请记住,我知道某些类可以在其Dispose()方法中执行不同操作。这个问题是关于 using 语句是否实现了与调用对象的 Dispose() 方法完全相同的结果。

Using statement vs. IDisposable.Dispose()

using基本上相当于:

try
{
  // code
}
finally
{
  obj.Dispose();
}

因此,它还具有调用Dispose()的好处,即使块内的代码中抛出未经处理的异常也是如此。

正如Brian Warshaw在这里所说,它只是tryfinally块的实现,以确保对象被释放。除了他的答案之外,using块还可以确保即使您使用范围返回内部,对象也会被处理掉。

我自己曾经对此感到好奇,并使用以下方法对其进行了测试:

自定义 ID是可操作的测试类和主

private class DisposableTest : IDisposable
{
    public string Name { get; set; }
    public void Dispose() { Console.WriteLine("{0}.Dispose() is called !", Name); }
}
public static void Main(string[] args)
{
    try
    {
        UsingReturnTest();
        UsingExceptionTest();                
    }
    catch { }
    try
    {
        DisposeReturnTest();
        DisposeExceptionTest();                
    }
    catch { }
    DisposeExtraTest();
    Console.ReadLine();
}        

测试用例实现

private static string UsingReturnTest()
{
    using (DisposableTest usingReturn = new DisposableTest() { Name = "UsingReturn" })
    {
        return usingReturn.Name;
    }
}
private static void UsingExceptionTest()
{
    using (DisposableTest usingException = new DisposableTest() { Name = "UsingException" })
    {
        int x = int.Parse("NaN");
    }
}
private static string DisposeReturnTest()
{        
    DisposableTest disposeReturn = new DisposableTest() { Name = "DisposeReturn" };
    return disposeReturn.Name;
    disposeReturn.Dispose(); // # IDE Warning; Unreachable code detected
}
private static void DisposeExceptionTest()
{
    DisposableTest disposeException = new DisposableTest() { Name = "DisposeException" };
    int x = int.Parse("NaN");
    disposeException.Dispose();
}
private static void DisposeExtraTest()
{
    DisposableTest disposeExtra = null;
    try
    {
        disposeExtra = new DisposableTest() { Name = "DisposeExtra" };
        return;
    }
    catch { }
    finally
    {
        if (disposeExtra != null) { disposeExtra.Dispose(); }
    }
}

输出为:

  • UsingReturn.Dispose() 被调用!
  • UsingException.Dispose() 被调用!
  • DisposeExtra.Dispose() 被调用!
//preceeding code
using (con = new Connection()) {
    con.Open()
    //do whatever
}
//following code

等效于以下内容(请注意 con 的范围有限):

//preceeding code
{
    var con = new Connection();
    try {
        con.Open()
        //do whatever
    } finally {
        if (con != null) con.Dispose();
    }
}
//following code

下面描述如下:http://msdn.microsoft.com/en-us/library/yh598w02.aspx

using 语句确保调用 Dispose 即使 在对象上调用方法时发生异常。您可以 通过将对象放入 try 块中来获得相同的结果,并且 然后在最终块中调用处置;事实上,这就是 using 语句由编译器翻译

using语句比try...finally{Dispose()}构造更清晰、更简洁,并且应该在几乎所有不想允许块在没有调用Dispose的情况下退出的情况中使用。 "手动"处置更好的唯一常见情况是:

  1. 方法调用工厂方法,该方法返回的内容可能会或可能不会实现"IDisposable",但如果实现,则应为"Dispose"d(非泛型"IEnumerable.GetEnumerator()"的情况)。 设计良好的工厂接口应该返回实现"IDisposable"的类型(可能使用什么都不执行的实现,就像"IEnumerator"的典型情况一样),或者指定调用方不应"释放"返回的对象。 不幸的是,一些接口,如非通用的"IEnumerable",不满足这两个标准。 请注意,在这种情况下不能很好地使用"using",因为它仅适用于声明类型实现"IDisposable"的存储位置。即使在退出块后,"IDisposable"
  2. 对象也应存在(在设置"IDisposable"字段或从工厂方法返回"IDisposable"时经常出现这种情况)。

请注意,从工厂方法返回IDisposable时,应使用如下所示的内容:

 布尔确定 = 假;  一次性类我的东西;  尝试  {    myThing = new DisposableClass();    ...    确定 = 真;    返回我的东西;  }  最后  {    如果(!ok)    {      if (myThing != null)        myThing.Dispose();    }  }

以确保myThing如果不返回,它将获得Dispose d。 我希望有一种方法可以使用using以及一些"取消处置"方法,但不存在这样的事情。

两者之间的区别在于,如果抛出异常

Con.Open()
'do whatever

Con.Dispose不会被召唤。

我不了解 VB 语法,但在 C# 中,等效代码将是

try
{
    con = new Connection();
    // Do whatever
}
finally
{
    if (con != null) con.Dispose();
}

using 块确保在引发异常时调用Dispose()

您的第二个示例不会这样做。

如果Con.Open()引发异常,则在第一种情况下,可以保证调用Con.Dispose()。 在第二种情况下,异常向上传播,不会调用Con.Dispose()

using 语句保证在引发异常时释放对象。这相当于在finally块中调用 dispose。

Using 将封闭的块包装在 try/finally 中,在 finally 块中调用 Dispose 。这可确保即使发生异常,也会调用 Dispose。

出于安全原因,您几乎在所有情况下都应使用

如果没记错的话,使用 可以保证一个对象被释放,而不管它周围的代码块如何退出它。它通过尝试包围块来做到这一点......最后阻止,并检查使用的变量是否为空,如果它不为空,则将其处理。如果引发异常,则允许它冒泡堆栈。除此之外,它所做的只是保证处理非空的一次性物品。

try
{
  var myDisposable = new DisposableObject();
  myDisposable.DoSomething();
}
finally
{
  if (myDisposable != null)
    ((IDisposable)myDisposable).Dispose();
}