方法返回一个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; } } 

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

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

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

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

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

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

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

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

将此与Image.FromStream的情况进行比较:在这种情况下,您传入一个流,然后Image假定拥有该流。 在这种情况下,你不能自己关闭流 – 你必须在完成后处理图像,它将处理流。

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

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

如果一个方法返回一个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); } 

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

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

是的,你不想让FileStream保持打开状态。 首先,您甚至无法在此之后自己打开文件。 调用Close()就足够了,但使用using可能是首选模式。

但是,您的代码存在更大的问题。 它无法在Windows上可靠地工作。 典型场景:

  • File.Open()调用成功。 你关闭它
  • 您的线程被Windows调度程序抢占
  • 另一个进程中的另一个线程有机会运行,它会打开文件
  • 您的线程重新获得CPU并在File.Open()调用后继续
  • 你打开文件,相信它会工作,因为IsOpen()返回false。

KABOOM。

永远不要写这样的代码,故障很难诊断。 只有在准备开始读取或写入文件时才打开文件。 在你完成之前不要关闭它。

额外奖励:现在很明显你想要使用using语句。

当您调用任何返回某些内容的方法时,正在创建该内容的实例。 只是因为你没有真正捕获它,所以不会让它变得“那里”。 因此,在IDisposable对象的情况下,该对象是通过您正在调用的方法创建的,尽管您没有对它执行任何操作。 所以是的,你仍然需要以某种方式处理它。 使用using语句的第一种方法似乎应该有效。