57

我的理解是,一旦代码退出块,.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()

4

9 回答 9

80

using基本上相当于:

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

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

于 2012-06-11T16:52:54.030 回答
20

正如Brian Warshaw这里所说的,它只是一个实现tryfinally块,以确保对象被释放。除了他的回答之外,usingblock 还确保即使您使用scope返回内部,该对象也已被处置。

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

自定义 IDisposable 测试类和 Main

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() 被调用!
于 2013-12-29T13:36:42.727 回答
9
//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 块中,然后在 finally 块中调用 Dispose 来获得相同的结果;事实上,编译器就是这样翻译 using 语句的

于 2012-06-11T18:30:57.903 回答
6

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

  1. 一个方法调用一个工厂方法,该方法返回的东西可能实现也可能不实现 `IDisposable`,但如果实现了,则应该是 `Dispose`d(这种情况发生在非泛型 `IEnumerable.GetEnumerator()` 中)。设计良好的工厂接口应该要么返回一个实现 `IDisposable` 的类型(可能是一个无所事事的实现,就像 `IEnumerator` 的典型情况),或者指定调用者不期望 `Dispose` 返回的对象。不幸的是,像非通用的 IEnumerable 等一些接口都不满足任何标准。请注意,在这种情况下不能很好地使用`using`,因为它仅适用于声明类型实现`IDisposable`的存储位置。
  2. `IDisposable` 对象即使在块退出后仍应存在(设置 `IDisposable` 字段或从工厂方法返回 `IDisposable` 时通常是这种情况)。

请注意,当IDisposable从工厂方法返回一个时,应该使用如下内容:

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

以确保如果它没有被退回,myThing它将得到d 。Dispose我希望有一种方法可以using与一些“取消处置”方法一起使用,但不存在这样的东西。

于 2012-06-11T19:06:24.190 回答
5

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

Con.Open()
'do whatever

Con.Dispose不会被调用。

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

try
{
    con = new Connection();
    // Do whatever
}
finally
{
    if (con != null) con.Dispose();
}
于 2012-06-11T16:52:38.787 回答
3

using 块确保Dispose()在抛出异常时调用它。

您的第二个样本没有这样做。

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

于 2012-06-11T16:52:31.997 回答
3

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

于 2012-06-11T16:52:49.630 回答
3

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

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

于 2012-06-11T16:54:09.163 回答
1

如果内存提供服务,则 using 可以保证对象被处置,而不管它周围的代码块如何退出它。它通过将块包围在 try...finally 块中,并检查使用的变量是否为空,如果它不为空则将其丢弃。如果抛出异常,则允许它冒泡堆栈。除此之外,它所做的只是保证处置非空一次性对象。

try
{
  var myDisposable = new DisposableObject();
  myDisposable.DoSomething();
}
finally
{
  if (myDisposable != null)
    ((IDisposable)myDisposable).Dispose();
}
于 2012-06-11T17:42:50.847 回答