4

MSDN 文档IDisposable指出:

IDisposable将接口添加到现有类是一个版本破坏性更改,因为它会更改类的语义。

究竟是什么意思?

我可以看到删除 IDisposable将是一件大事。例如,在语句中实例化一个类using将不再可能。但是还有什么IDisposable特别之处,尤其是在添加界面的情况下?

4

3 回答 3

7

当你实现时IDisposable,你基本上是在添加“你应该在完成后处理它”的语义。这意味着处理它的现有代码违反了该隐式合同......因此,在保持客户正确使用您的类型的同时,没有办法从“不实现IDisposable”到“实现IDisposable”,除非您也可以访问所有该客户端代码。

再举一个例子——假设文档GetHashCode被更新为“这应该总是返回一个偶数”——这将是一个重大变化,因为现有代码很容易返回一个奇数。同样,它不会破坏源代码- 一切仍然会构建 - 但您将更改合同以使其更具限制性。

如果您将 的实现视为客户端IDisposable代码上的合同,希望更清楚为什么它是一项重大更改。

于 2013-08-12T17:09:05.307 回答
1

如果一个类没有实现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不会向客户端代码添加任何新职责——它只会让客户端代码更容易真正关心它无论如何都会拥有的职责(检查对象实例是否实现IDisposableIDisposable.Dispose无条件调用更慢更麻烦在将其实现为无操作方法的类上)。

于 2013-08-12T19:16:28.903 回答
1

实现IDisposable告诉客户他们总是需要处理类。

这是一个突破性的变化。

于 2013-08-12T17:09:29.210 回答