MSDN 文档IDisposable
指出:
IDisposable
将接口添加到现有类是一个版本破坏性更改,因为它会更改类的语义。
这究竟是什么意思?
我可以看到删除 IDisposable
将是一件大事。例如,在语句中实例化一个类using
将不再可能。但是还有什么IDisposable
特别之处,尤其是在添加界面的情况下?
MSDN 文档IDisposable
指出:
IDisposable
将接口添加到现有类是一个版本破坏性更改,因为它会更改类的语义。
这究竟是什么意思?
我可以看到删除 IDisposable
将是一件大事。例如,在语句中实例化一个类using
将不再可能。但是还有什么IDisposable
特别之处,尤其是在添加界面的情况下?
当你实现时IDisposable
,你基本上是在添加“你应该在完成后处理它”的语义。这意味着不处理它的现有代码违反了该隐式合同......因此,在保持客户正确使用您的类型的同时,没有办法从“不实现IDisposable
”到“实现IDisposable
”,除非您也可以访问所有该客户端代码。
再举一个例子——假设文档GetHashCode
被更新为“这应该总是返回一个偶数”——这将是一个重大变化,因为现有代码很容易返回一个奇数。同样,它不会破坏源代码- 一切仍然会构建 - 但您将更改合同以使其更具限制性。
如果您将 的实现视为客户端IDisposable
代码上的合同,希望更清楚为什么它是一项重大更改。
如果一个类没有实现IDisposable
,使用该类的代码可能会创建和放弃实例,而不检查它们是否实现IDisposable
。即使该类的更高版本确实实现了IDisposable
,该更改也不会神奇地导致从不检查的代码IDisposable
神奇地开始调用IDisposable.Dispose
;如果可能,显式检查IDisposable
并尝试调用的客户端代码Dispose
(例如,与块关联的编译器生成的代码foreach
)将在可能时开始调用IDisposable.Dispose
,但大多数代码不会打扰。
请注意,并非所有使用对象的代码都必须担心它的实现IDisposable
。唯一应该调用IDisposable
一个对象的时间是它是否是最后一个持有对它的“有用”引用的东西。如果代码为方法提供了对对象的引用,并且该方法在返回后不会对对象执行任何操作,则调用者将很容易知道该方法何时使用对象完成,因此调用者将同样能够调用Dispose
方法。因此,该方法没有理由调用 Dispose。另一方面,如果代码调用一个对象的实现IEnumerable<T>.GetEnumerator
并且该方法需要一个对象的服务,一旦枚举完成就需要清理,那么实现对象的唯一方法GetEnumerator
当不再需要服务时,如果GetEnumerator
调用的代码Dispose
在不再需要返回的枚举器时调用它,则可能是这样。
顺便说一句,是否需要调用IDisposable.Dispose
工厂方法返回的对象GetEnumerator
并不一定取决于工厂方法的返回类型。如果在对象上调用非泛型IEnumerable.GetEnumerator()
,语义正确性要求检查返回的对象实例是否实现IDisposable
(它可能会这样做,即使声明的类型IEnumerable.GetEnumerator()
没有),Dispose
如果是则调用它。IDisposable
从这个角度来看,在早期版本没有实现抽象基类的更高版本时,它可能并不是真正的重大变化。如果调用返回抽象类的工厂方法的代码需要在可能的情况下调用Disposed
返回的项目,则让基类实现IDisposable
不会向客户端代码添加任何新职责——它只会让客户端代码更容易真正关心它无论如何都会拥有的职责(检查对象实例是否实现IDisposable
比IDisposable.Dispose
无条件调用更慢更麻烦在将其实现为无操作方法的类上)。
实现IDisposable
告诉客户他们总是需要处理类。
这是一个突破性的变化。