c# / . net编译器不警告Dispose()的原因是什么?

本文关键字:是什么 Dispose net 编译器 警告 | 更新日期: 2023-09-27 18:09:47

我今天在写IDisposable代码的时候想到了这个问题。

对于开发人员来说,直接调用Dispose()是一个很好的实践,或者如果对象的生命周期允许,使用using结构。

我们唯一需要担心的实例是那些由于代码机制而不能使用using的实例。但是,在某些时候,我们应该在这些对象上调用Dispose()

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

有什么原因吗,或者有什么想法沿着这条路走下去吗?

c# / . net编译器不警告Dispose()的原因是什么?

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

它可以确定在某些简单的情况下,Dispose将永远不会被调用。仅基于代码的静态分析,不能确定所有创建的实例将被处理。代码也不需要非常复杂,甚至可以直接估计对象是否未被处理。

更糟的是,不是所有的IDisposable对象实例都应该被处置。造成这种情况的原因有很多。有时一个对象实现了IDisposable,即使只有一部分实例在实现中做了任何事情。(IEnumerator<T>就是一个很好的例子。大量的实现在被处置时什么也不做,但有些实现做了。如果你知道你所拥有的具体实现永远不会做任何处理,你就不用麻烦了;如果你不知道,你需要确保调用Dispose

还有一些类型,如Task,实际上几乎不需要处理。(参见我需要处理任务吗?)在绝大多数情况下,您不需要处理它们,并且不必要地使用using块或不做任何事情的dispose调用来混淆代码会妨碍可读性。

关于IDisposable的主要规则是"请最后一个离开房间的人关灯"。大多数。net语言设计的一个主要缺点是没有通用的语法(甚至属性标签)约定来指示持有特定变量的代码或持有特定字段的类是否会:

    总是最后一个离开房间的人永远不要做最后一个离开房间的人
  1. 有时是最后一个离开房间的人,并且在运行时很容易知道它是否会离开(例如,因为给它引用的人告诉它)。
  2. 可能是最后一个离开房间的人,但在离开房间之前不知道自己是不是最后一个出去的人。

如果语言有一种语法来区分这些情况,那么编译器就可以很容易地确保那些知道自己将是最后一个离开房间的人关灯,而那些永远不会是最后一个离开房间的人不关灯。如果框架包含编译器知道的包装器类型,那么编译器或框架可以促进第三和第四种情况。传统的引用计数通常不是确定对象何时不再需要的主要机制,因为它需要处理器在每次复制或销毁引用时互锁,即使副本的持有者知道它不会是"最后一个离开房间的人"。但是,引用计数的变化通常是处理场景#4的最便宜和最实用的方法[复制引用时,只有当原始和副本的持有者都认为自己可能是最后一个所有者时,才应该增加计数器;销毁引用的副本时,只有当副本创建时引用已经增加时,才应该减少计数器]。

如果没有一个约定来指示一个特定的引用是否应该被认为是"房间里的最后一个",编译器就没有好方法来知道该引用的持有者是否应该"关灯"(即调用Dispose)。VB。NET和c#有一个特殊的using语法,用于一种特殊的情况,即变量的持有者知道它将是最后一个离开房间的人,但除此之外,如果编译器不理解它们,它们就不能真正要求清理这些东西。c++/CLI确实有一种更通用的语法,但不幸的是,它在使用上有许多限制。

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

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