使用语句与IDisposable.Dispose()

osc*_*tin 49 .net c# vb.net idisposable using

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

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

Bri*_*haw 66

using 基本上相当于:

try
{
  // code
}
finally
{
  obj.Dispose();
}
Run Code Online (Sandbox Code Playgroud)

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

  • @EricJ.,这个答案不是编程问题的解决方案 - 它是对使用`using`语句和块时会发生什么的简单解释. (3认同)

Sar*_*yan 20

正如Brian Warshaw这里所说,它只是一个实现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();
}        
Run Code Online (Sandbox Code Playgroud)

测试用例实现

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(); }
    }
}
Run Code Online (Sandbox Code Playgroud)

输出是:

  • 调用UsingReturn.Dispose()!
  • 调用UsingException.Dispose()!
  • 调用DisposeExtra.Dispose()!


hat*_*ica 9

//preceeding code
using (con = new Connection()) {
    con.Open()
    //do whatever
}
//following code
Run Code Online (Sandbox Code Playgroud)

相当于以下(注意con的范围有限):

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

这里描述:http://msdn.microsoft.com/en-us/library/yh598w02.aspx

using语句确保即使在对象上调用方法时发生异常,也会调用Dispose.您可以通过将对象放在try块中然后在finally块中调用Dispose来实现相同的结果; 实际上,这就是编译器如何翻译using语句.


sup*_*cat 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将获得Disposed.我希望有一种方法可以使用using一些"取消Dispose"方法,但不存在这样的事情.


Eri*_* J. 5

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

Con.Open()
'do whatever
Run Code Online (Sandbox Code Playgroud)

Con.Dispose 不会被叫.

我不是用VB语法,但在C#中,等效的代码就是

try
{
    con = new Connection();
    // Do whatever
}
finally
{
    if (con != null) con.Dispose();
}
Run Code Online (Sandbox Code Playgroud)

  • 你的代码是borken.当Connection构造函数抛出异常时,终结器将弹起,"con"为null.将其移出try块.*using*语句还确保重新赋值变量不会造成麻烦. (3认同)