IDisposable
的MSDN文档说明:
向现有类添加
IDisposable
接口是一种破坏版本的更改,因为它改变了类的语义。
这到底意味着什么?
我可以看出来,删除 IDisposable
将是一个大问题。例如,在 using
语句中实例化一个类将不再可能。但是除此之外,还有什么使 IDisposable
特别,特别是在添加接口的情况下?
IDisposable
的MSDN文档说明:
向现有类添加
IDisposable
接口是一种破坏版本的更改,因为它改变了类的语义。
这到底意味着什么?
我可以看出来,删除 IDisposable
将是一个大问题。例如,在 using
语句中实例化一个类将不再可能。但是除此之外,还有什么使 IDisposable
特别,特别是在添加接口的情况下?
IDisposable
接口时,基本上是添加了“当你使用完之后应该进行清理”的语义。这意味着,那些没有执行清理的现有代码违反了这个隐含的契约......因此,除非你也可以访问所有客户端代码,否则从“不实现IDisposable
”到“实现IDisposable
”是无法确保客户端使用你的类型正确的。GetHashCode
的文档更新为“这应该始终返回偶数” - 这将是一个破坏性的变化,因为现有代码可能会返回奇数。同样,它不会在源代码中引起错误——一切都仍然可以编译——但你将改变契约以使其更加严格。IDisposable
的实现视为对客户端代码的契约,希望这能更清楚地说明为什么这是一个破坏性的变化。实现 IDisposable
告诉客户端他们总是需要处理该类。
这是一个破坏性的变更。
Dispose()
的客户端。 - Jonathon ReinhartIDisposable
,使用该类的代码很可能会创建并且放弃实例而不检查它们是否实现了IDisposable
。即使类的后续版本实现了IDisposable
,也不会自动导致从未检查IDisposable
的代码自动开始调用IDisposable.Dispose
;只有客户端代码明确检查IDisposable
并尝试在可能的情况下调用Dispose
(例如与foreach
块相关联的编译器生成的代码)才会在可能的情况下开始调用IDisposable.Dispose
,但大多数代码不会这样做。IDisposable
。唯一应该在对象上调用IDisposable
的时间是,如果它是最后一个持有“有用”引用的东西。如果代码给方法一个对象的引用,并且该方法在返回后不会对该对象执行任何操作,则调用者将很容易知道方法何时完成了对该对象的操作,因此调用者将能够像方法一样调用Dispose
。因此,方法没有理由调用Dispose。另一方面,如果代码调用对象的IEnumerable<T>.GetEnumerator
实现,并且该方法需要清理枚举完成后将需要清理的对象的服务,则实现GetEnumerator
的对象可能仅当调用GetEnumerator
的代码在不再需要枚举器时在返回的枚举器上调用Dispose
时才能知道服务不再需要。GetEnumerator
这样的工厂方法返回的对象上是否需要调用IDisposable.Dispose
并不一定取决于工厂方法的返回类型。如果在对象上调用非泛型的IEnumerable.GetEnumerator()
,则语义正确性要求检查返回的对象实例是否实现了IDisposable
(即使声明类型IEnumerable.GetEnumerator()
没有实现),如果是,则调用其上的Dispose
。从这个角度来看,当较早版本未这样做时,较晚版本的抽象基类实现IDisposable
可能并不真正是一个破坏性的改变。如果要求调用返回抽象类的工厂方法的代码在可能的情况下调用Disposed
,则让基类实现IDisposable
不会为客户端代码增加任何新的责任——它只会使客户端代码更容易实际履行本来就应该履行的责任(检查对象实例是否实现IDisposable
比在实现它作为一个无效方法的类上无条件地调用IDisposable.Dispose
更慢、更繁琐)。
using
)。你的解释非常合理。 - Jonathon Reinhart