有没有理由C#/ .NET编译器没有警告Dispose()?

时间:2013-04-17 19:30:45

标签: c# .net vb.net idisposable

在我写一些IDisposable代码的时候,我正在考虑这个问题。

开发人员可以直接调用Dispose(),或者如果对象的生命周期允许,则使用using构造。

我们需要担心的唯一情况是,由于代码的机制,我们无法使用using。但是,在某些时候,我们应该在这些对象上调用Dispose()

鉴于C#编译器知道一个对象实现了IDisposable,理论上它也可以知道Dispose()从未被调用过它(它是一个非常聪明的编译器!)。它可能不知道程序员何时应该这样做的语义,但它可以作为一个很好的提醒,它永远不会被 调用,因为它从未在using构造中使用,并且方法Dispose()从未在任何实现IDisposable的对象上直接调用。

有什么理由,还是有想过走这条路?

3 个答案:

答案 0 :(得分:2)

  

理论上它也可以知道Dispose()从未被调用过它

在某些简单的情况下,它可以确定永远不会调用Dispose。仅仅基于代码的静态分析,可以确定所有创建的实例都将被处理掉。代码也不需要非常复杂,以达到甚至估计对象是否未被遮挡的程度是很容易做到的。

更糟糕的是,并非所有IDisposable个对象实例都应该被处理。这可能有很多原因。有时,对象实现IDisposable,即使它们的实例中只有一部分实际上在实现中做了任何事情。 (IEnumerator<T>就是一个很好的例子。大量的实现在处理时什么都不做,但有些实现了。如果你知道具体的实现,你将不会做什么处理,你不能打扰;如果你不知道你需要确保拨打Dispose

然后有一些类型,例如Task几乎从未真正需要处理。 (请参阅Do I need to dispose of Tasks?。)在绝大多数情况下,您不需要处理它们,并且使用using块不必要地混乱您的代码或处理不会阻碍可读性的调用。

答案 1 :(得分:1)

代码分析规则将检测到这一点。根据您的VS版本,您可以使用FXCop或内置分析规则。

它需要在编译后对代码进行静态分析。

答案 2 :(得分:1)

关于IDisposable的主要规则是“最后一个离开房间,请关灯”。在大多数.NET语言的设计中,一个主要的失败是没有通用的语法(甚至属性标记)约定来指示包含特定变量或包含特定字段的类的代码是否会:

  1. 永远是最后一个离开房间的人
  2. 绝不是最后一个离开房间的人
  3. 有时候是离开房间的最后一个人,并且在运行时很容易知道是否会出现这种情况(例如,因为谁给了它一个参考资料告诉它)。
  4. 可能是最后一个离开房间的人,但在离开房间之前不知道是否会是最后一个。

如果语言具有区分这些情况的语法,那么编译器可以很容易地确保知道它们将成为最后一个离开房间的事物变成灯光和永远不会发生的事情将成为最后一个离开房间的人不要关灯。如果框架包含编译器知道的包装类型,则编译器或框架可以促进第三和第四场景。传统的引用计数通常不是确定何时不再需要对象的主要机制,因为每次复制或销毁引用时它都需要处理器互锁,即使副本的持有者知道它不是“最后一个离开房间“,但参考计数的变化通常是处理场景#4的最便宜和最实用的方法[复制参考只应增加计数器,如果原始和副本的持有者将要思考他们可能是最后一个所有者,并且如果在创建该副本时增加了引用,则销毁引用的副本只应递减计数器。

如果没有一个约定来表明特定的引用是否应被视为“房间中的最后一个”,那么编译器就没有好的方法来知道该引用的持有者是否应该“关灯”(即致电Dispose)。 VB.NET和C#都有一个特殊的using语法用于一个特定情况,其中变量的持有者知道它将是最后一个离开房间的人,但除此之外,编译器不能真正要求事情是如果他们不理解他们就会清理干净。 C ++ / CLI确实有一个更通用的语法,但不幸的是它对它的使用有很多限制。