方法返回一个IDisposable - 我应该处理结果,即使它没有分配给任何东西?

mat*_*att 5 c# idisposable

这似乎是一个相当简单的问题,但在搜索之后我找不到这个特殊的用例.

假设我有一个简单的方法,比如确定某个进程是否打开了一个文件.我可以这样做(不是100%正确,但相当不错):

public bool IsOpen(string fileName)
{
  try
  {
    File.Open(fileName, FileMode.Open, FileAccess.Read, FileShare.None);
  }
  catch
  {
    // if an exception is thrown, the file must be opened by some other process
    return true;
  } 
}
Run Code Online (Sandbox Code Playgroud)

(显然,这不是确定这一点的最佳或甚至是正确的方法 - File.Open会抛出许多不同的异常,所有异常都具有不同的含义,但它适用于此示例)

现在File.Open调用返回a FileStream,并FileStream实现IDisposable.通常我们想要FileStream在使用块中包装任何实例化的用法,以确保它们被正确处理掉.但是在我们实际上没有将返回值分配给任何东西的情况下会发生什么?是否仍然需要处理FileStream,如下:

try
{
  using (File.Open(fileName, FileMode.Open, FileAccess.Read, FileShare.None));
  { /* nop */ }
}
catch 
{
  return true;
}
Run Code Online (Sandbox Code Playgroud)

我应该创建一个FileStream实例并处理它吗?

try
{
  using (FileStream fs = File.Open(fileName, FileMode.Open, FileAccess.Read, FileShare.None));
}
...
Run Code Online (Sandbox Code Playgroud)

或者这些完全没必要?我们可以简单地调用File.Open而不是将它分配给任何东西(第一个代码示例),让GC立即处理它吗?

Jon*_*eet 15

是的,你应该放弃FileStream.否则,流将保持打开状态,并且文件将无法使用,直到终结器正在清理它.

这里重要的是所有权:因为File.Open,调用者被假定为"拥有"返回给它的流 - 如果你拥有实现的东西IDisposable,那么你有责任处理它.

将此与以下情况进行比较Image.FromStream:在这种情况下,您传入一个流,Image然后假定它拥有该流.你一定不要自己关闭流,在这种情况下-你有当你完成处置的形象,它会处理流的.

调用返回一次性内容的静态方法几乎总是假定调用者拥有资源的所有权.同上构造函数(有效的静态方法.)

具有讽刺意味的是,如果您不处理返回的流File.Open,您将发现该文件可用 - 同时使其无法使用直到某些不确定的时间.


sta*_*ica 5

如果一个方法返回一个IDisposable,我个人总是将它放在一个using块中.即使我没有将返回值分配给任何东西.

即使您没有将其分配给变量,仍会创建一次性对象.Dispose是不是会被自动调用.唯一的区别是返回的对象将立即有资格进行垃圾收集,因为没有(强)引用它.

Dispose回收对象时,垃圾收集器不会自动调用.但是,大多数IDisposable类型提供终结器(将在GC回收对象之前调用)Dispose作为回退策略(安全网)调用- 研究IDisposable模式以查看如何完成此操作:

~SomeClass          // <-- the finalizer method will usually call Dispose;
{                   //     but you have no control over when it will be called!
    Dispose(false);
}
Run Code Online (Sandbox Code Playgroud)

请记住,您不知道垃圾收集器何时运行(因为它是非确定性的).因此,您也不知道何时调用终结器方法.正因为如此 - 如果你没有Dispose明确地调用(无论是你自己,还是一个using块) - 你不知道什么时候它会被终结器调用.

这是明确调用的优势Dispose:您可以释放资源 - 或者至少允许GC释放托管资源 - 只要您完成它们,而不是保留资源,直到将来某个时候调用终结器.