未使用的IDisposable返回值是否需要处理?

4

有一些静态方法,比如Process.Start()File.Create()会构造并返回IDisposable实例,但这些实例通常被丢弃。我们很容易使用这些方法,就好像它们返回了void一样,如果你不注意,你可能会错过它们实际上有返回值的事实。

我知道最佳实践是始终处理IDisposable实例。那么未使用的返回值是否也适用呢?你应该总是写Process.Start(processPath).Dispose();而不是Process.Start(processPath);吗?

我认为这显然是肯定的,但我必须反复思考自己,因为我只看到这样做时没有Dispose()。C#也许有一些自动处理这些情况的方式吗?


1
所有通常的考虑在这里同样适用,这些情况并不是例外。而且,我们不应该认为使用这些方法就像它们返回void一样正常。 - Evk
2
是的,有一种自动处理方式:如果一个类本身封装了非托管资源(比如进程句柄),它将拥有一个终结器,在垃圾回收器到达时负责释放这些资源。显然,这不如在使用完毕后立即处理事物好。你经常看到人们变得懒散的原因是因为代码只需运行一次,或者在退出之前永远不会创建多个进程或文件。在这种情况下,没有立即处理的事实是不被注意到的。 - Jeroen Mostert
1
无论对象是由您发起的调用返回还是由您编写的代码初始化,都不重要。重要的是不清理资源的后果。 - P.Brian.Mackey
4
不应该使用 Process.Start(processPath).Dispose(),而应该使用 using(var p = Process.Start(processPath)) {}。可能会出现 Start 抛出异常的情况,在这种情况下,后一种方法将确保其被正确处理。 - Tim Schmelter
1
@HansPassant 我不认为这是正确的。它立即分配进程句柄,并且释放它是主要任务。难道不是吗? - Evk
显示剩余3条评论
2个回答

6

在大多数情况下,处理IDisposable实例的首选方式是使用using语句:

using (var file = File.Create())
{
   //use file instance
}

这种语法糖会确保在退出using语句块后,文件实例会被显式处理。在底层,它实际上将您的代码包装到try/finally块中。


2
但不要使用 HttpClient 进行操作,而是创建一个应用程序范围内的静态实例 :D - ProgrammingLlama
这是同一个问题吗?- https://dev59.com/lk7Sa4cB1Zd3GeqP3W2o?rq=1 - Mauricio Gracia Gutierrez
3
这个答案没有完全反映我的问题。你在谈论的情况是返回值被分配给一个变量并被使用的情况。我说的是返回值被未使用的情况。在这种情况下,使用代码块将为空。这感觉太笨拙了,我不愿称其为语法糖。 - Kyle Delaney
1
@KyleDelaney 我同意,我必须更加精确,最好的方式是无论特定的用例如何,创建的实例都应该被处理 :) - Darjan Bogdan
1
我知道 using 相对于 Dispose() 有一些微妙的优势,但是空块看起来像是一个错误,所以为了提高可读性,我可能仍然会选择 Dispose() - Kyle Delaney
显示剩余2条评论

5
.NET有一个Finalizer的概念,许多BCL类型实现IDisposable接口时也同时实现了Finalizer。您可以查看MSDN Dispose Pattern文章和这篇CodeProject帖子以获取更多详细信息。因此,如果您不手动释放某个对象,它很可能会在以后被释放。问题是,您是否可以接受稍后(以及其他潜在的问题),或者需要在特定时间点明确且确定地释放它。
跳过Dispose方法调用并依靠Finalizer的缺点在线程中进行了描述,包括:
  1. 性能问题
  2. 锁定资源的问题,这些资源的使用时间比您预期的要长
  3. 即使有异常处理策略,仍存在崩溃风险
总的来说,我强烈建议始终显式调用Dispose方法以避免奇怪的问题。
但是,您应该只释放自己拥有的那些对象(您创建并知道何时不再使用)。我曾遇到这样的情况,当使用某些IDisposable资源时,将其作为属性获取另一个IDisposable对象,显然在停止使用引用对象之前不应该释放对象的属性 - 当被释放时,它会自动处理。

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