使用语句与IDisposable.Dispose()

时间:2012-06-11 16:51:07

标签: c# .net vb.net using idisposable

我的理解是,一旦代码退出块,.NET中的using statement就会调用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()方法完全相同的结果。

9 个答案:

答案 0 :(得分:63)

using基本上相当于:

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

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

答案 1 :(得分:18)

正如 Brian Warshaw here中所述,它只是tryfinally块的实现,以确保处置对象。除了他的答案之外,using阻止还确保即使您使用范围 返回内部也会处置该对象。

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

自定义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()!

答案 2 :(得分: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语句由编译器翻译

答案 3 :(得分:6)

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

  1. 一个方法调用一个工厂方法,它返回一些可能实现或不实现`IDisposable`的东西,但是如果它实现的话应该是`Dispose`d(与非泛型`IEnumerable.GetEnumerator()`一起出现的场景) 。精心设计的工厂接口应该返回一个实现`IDisposable`的类型(可能带有一个无操作的实现,通常是`IEnumerator`的情况),否则指定调用者不应该`Dispose`返回的对象。不幸的是,像非通用的“IEnumerable”这样的接口既不满足标准。请注意,在这种情况下,不能很好地使用`using`,因为它只适用于声明类型实现`IDisposable`的存储位置。
  2. 即使在退出块之后,`IDisposable`对象仍然存在(通常是在设置`IDisposable`字段或从工厂方法返回`IDisposable`时)。

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

  bool ok = false;
  DisposableClass myThing;
  try
  {
    myThing = new DisposableClass();
    ...
    ok = true;
    return myThing;
  }
  finally
  {
    if (!ok)
    {
      if (myThing != null)
        myThing.Dispose();
    }
  }

确保myThing如果没有返回Dispose将获得using。我希望有一种方法可以使用{{1}}以及一些“取消Dispose”方法,但不存在这样的事情。

答案 4 :(得分:5)

两者之间的区别在于,如果在

中抛出异常
Con.Open()
'do whatever

Con.Dispose将不会被调用。

我没有使用VB语法,但在C#中,等效代码将是

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

答案 5 :(得分:3)

using语句保证在抛出异常的情况下处理对象。这相当于在finally块中调用dispose。

答案 6 :(得分:3)

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

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

答案 7 :(得分:2)

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

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

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

答案 8 :(得分:1)

如果内存服务,则使用是保证对象被丢弃,无论它所围绕的代码块是如何退出的。它通过在try ... finally块中包围块来执行此操作,并检查使用的变量是否为null,然后在它不为null时处理它。如果抛出异常,则允许冒泡堆栈。除此之外,它所做的只是保证处理非零的一次性物品。

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