使用关键字和IDisposable接口之间的关系是什么?

5

如果我使用using关键字,我是否仍然需要实现IDisposable接口?


3
也许你想问的是,“如果我使用 'using' 关键字,我是否仍然需要调用 Dispose() 方法?” - Richard Anthony Freeman-Hein
4
@Chris Farmer...编辑得不错。我开始觉得这是@Apple的新垃圾压缩机!!! - user279521
正如其他人所指出的,using语句的控制变量必须是实现了IDisposable接口的类型。然而,某些方法包含using语句并不意味着包含这些方法的类必须实现IDisposable接口。类实现IDisposable接口最常见的原因是它们包含的资源的生命周期无法在using语句中封装。 - supercat
7个回答

13

如果您使用 using 语句,那么所包含的类型必须已经实现了 IDisposable 接口,否则编译器将会发出错误。因此,将 IDisposable 实现视为使用的先决条件。

如果您想在自定义类上使用 using 语句,则必须为其实现 IDisposable 接口。但这样做有点本末倒置,因为没有理由只是为了这个目的而这样做。只有当您有需要处理像非托管资源这样的东西时,才应该实现它。

// To implement it in C#:
class MyClass : IDisposable {

    // other members in you class 

    public void Dispose() {
        // in its simplest form, but see MSDN documentation linked above
    }
}

这使您能够:

using (MyClass mc = new MyClass()) {

    // do some stuff with the instance...
    mc.DoThis();  //all fake method calls for example
    mc.DoThat();

}  // Here the .Dispose method will be automatically called.

实际上这与编写以下内容相同:

MyClass mc = new MyClass();
try { 
    // do some stuff with the instance...
    mc.DoThis();  //all fake method calls for example
    mc.DoThat();
}
finally { // always runs
    mc.Dispose();  // Manual call. 
}

1
@Troy:添加了示例以突出说明。 - John K
2
实际上,每当您需要在finally部分执行某些操作的“三明治”代码时,这是一种非常有用的模式。例如,常见情况是繁忙的光标,步骤如下... 1./ 复制现有光标 2./ 将光标设置为等待光标 3./ 在finally部分复制回原始光标。使用using和IDisposable,您可以编写类似以下代码 using(Busy.WaitCursor) { //其中WaitCursor是返回Busy实例的静态属性,构造函数复制并设置Dispose()重新设置。 } - Tim Jarvis
@Tim - 另一个很好的例子是更改控制台应用程序的颜色(我想我在Wes Dyer的博客上看到过)。对于这样的任务,有时实现一个仅包装在Dispose()上执行的Action的可处理类是有用的。然而,我不禁觉得有时它有点反模式。我想知道其他人对通用可处理类的看法如何。 - Alex Humphrey

13

你不能只有其中一个而没有另一个。

当你写下以下代码:

using(MyClass myObj = new MyClass())
{
    myObj.SomeMthod(...);
}

编译器将生成类似这样的代码:

MyClass myObj = null;
try
{
    myObj = new MyClass();
    myObj.SomeMthod(...);
}
finally
{
    if(myObj != null)
    {
        ((IDisposable)myObj).Dispose();
    }
} 

所以可以看到,在使用 using 关键字时,假定/要求已实现 IDisposable。


+1,但不是默认的-必需的。您在此处显示的编译器代码将在类型未实现IDisposable时引发无效转换异常。这就是为什么编译器通过给出编译时错误来保护此问题,使IDisposable在使用using时成为必需而不是默认情况。 - Jimmy Hoffa
@Jimmy,是的,你说得对,我是指那个意思。回答已经更新了。 - Incognito

5

你有些混淆了。只有实现了IDisposable接口的对象才能使用"using"关键字。

补充:如果使用了"using"关键字,就不需要显式调用Dispose方法,在using块结束时它会自动被调用。其他人已经发布了将using语句转换为try-finally语句的示例,并在finally块中调用Dispose的方式。


当从记录集中读取数据时,您可以使用“using”关键字,对吗? - Troy
1
@Troy,是的,那是因为IDataReader实现了IDisposable接口。 - Tim Jarvis
只有当包装记录集的任何内容实现了IDisposable接口时,才能使用它。如果代码可以编译通过,则说明它已经实现了IDisposable接口... - Richard Anthony Freeman-Hein

4

是的,using关键字是这种模式的语法糖...(来自msdn)

  Font font1 = new Font("Arial", 10.0f);
  try
  {
    byte charset = font1.GdiCharSet;
  }
  finally
  {
    if (font1 != null)
      ((IDisposable)font1).Dispose();
  }

编辑:一个有用的例子。

当您发现自己在finally部分做一些事情时,例如将光标重置回默认值,而在设置为等待光标后,这是该模式的候选人...

  public class Busy : IDisposable
  {

    private Cursor _oldCursor;

    private Busy()
    {
      _oldCursor = Cursor.Current;
    }

    public static Busy WaitCursor
    {
      get
      {
        Cursor.Current = Cursors.WaitCursor;
        return new Busy();
      }
    }

    #region IDisposable Members

    public void Dispose()
    {
      Cursor.Current = _oldCursor;
    }

    #endregion
  }

被称为...

using(Busy.WaitCursor)
{
  // some operation that needs a wait cursor.
}

2
使用using只会处理可被释放的对象。因此,将using块包装在不实现IDisposable接口的对象周围是毫无意义的,实际上会导致编译器错误。
通常情况下,当你使用一个IDisposable对象时,应该在using语句中声明和实例化它。using语句以正确的方式调用Dispose方法,并在调用Dispose后使对象本身超出范围。在using块内,对象是只读的,不能被修改或重新分配。 using语句确保即使在调用对象的方法时发生异常,Dispose也会被调用。您可以通过将对象放在try块中,然后在finally块中调用Dispose来实现相同的结果;实际上,这就是编译器如何翻译using语句的方式。
来源:http://msdn.microsoft.com/en-us/library/yh598w02.aspx

1

using 关键字已经实现,所以如果你使用 using 关键字,你就不需要调用 IDisposable


1
你必须实现IDisposable才能使用using。如果您尝试在不实现IDisposable的类型上使用using(),则会收到以下编译时错误:
error CS1674: 'SomeType': type used in a using statement must be implicitly convertible to 'System.IDisposable'

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接