保守的 ID是接口上的可标记
本文关键字:ID 接口 | 更新日期: 2023-09-27 18:19:54
在可能需要或不需要处置接口的实现的情况下,要求必须通过将接口本身标记为IDisposable
来释放所有实现是否是一个好主意?
这会强制此接口的用户始终释放对象,即使对于某些实现可能是不必要的。 目标显然是确保实现始终可以无缝交换。 另一种方法是将其留给用户决定,这似乎更正确,但更难沟通。 另一方面,考虑到用户只有一个用接口类型声明的符号,要求无条件释放它似乎更正确。 这种推理是否存在权衡或谬误?
如果问题过于依赖于情况,我想将其置于教科书存储库(存储库设计模式(的上下文中,其中大多数已知的实现都需要处理。
如果问题过于依赖于情况,我想将其置于教科书存储库(存储库设计模式(的上下文中,其中大多数已知的实现都需要处理。
如果您正在编写实例化对象并负责其生存期的库代码,那么理想的方案是处理IDisposable
,但不要要求它,除非大多数实现极有可能需要清理;例如,在代码完成未知对象时使用 is
或 as
。它很少像这样简单,但例如:
IFoo obj = (IFoo)Activator.CreateInstance(unknownType);
using(obj as IDisposable)
{
// TODO: code
}
在决定IFoo
是否应该实现IDisposable
时,关键问题不应该是实现IFoo
的很大一部分类型是否需要处置,甚至不是实现IFoo
的对象实例的很大一部分需要处置,而是是否需要处置由返回类型为 IFoo
的工厂方法返回的任何非平凡的实例由该工厂方法的调用方提供。
考虑IEnumerator
. 只有一小部分实现它的类需要处置(随着迭代器的引入,这个部分可能会显着增加(,但绝大多数实例将通过调用工厂方法IEnumerable.GetEnumerator()
获得,其返回类型为 IEnumerator
。 调用IEnumerable.GetEnumerator()
的代码通常不知道返回的对象是否需要处置;对于语义正确性,除非代码知道它使用的IEnumerable
类型不会返回需要处置的实例,否则为了正确性,它必须尝试将返回的实现强制转换为IDisposable
,如果结果为非 null,则释放它们。
当Microsoft实现IEnumerator<T>
时,他们意识到消费者必须尝试将枚举器转换为IDisposable
的负担远远大于实施者必须实现无所事事Dispose
方法的负担。 语义正确性要求消费者在任何实现IDisposable
的枚举器上调用Dispose
,并且无条件地在已知实现它的类型上调用无所事事Dispose
方法比测试可能实现IDisposable
的类型是否这样做更容易和更快。
即使只有1%的IEnumerator<T>
实现实现了IDisposable
,为了语义正确性,绝大多数消费者必须假设他们中的任何一个都可能。 拥有IEnumerable<T>
继承IDisposable
不会给消费者带来任何他们本来不会承担的义务——它只是让他们更容易履行他们在任何情况下都会承担的义务。
因此,您正在公开一个接口
ISomeInterface
如果你打算用这个接口来触发处置,即你想调用Dispose
,那么,它应该实现IDisposable
。
ISomeInterface : IDisposable
但是,考虑到无处不在的IDisposable
的组合肯定会导致ISomeInterface
实现的第 3 方用户显式或using
块调用Dispose
themsevles。
如果您不打算自己调用Dispose
,请将选择权留给具体的类实现者。只有他们才能决定在特定情况下是否需要处置。
现在我可以引用一个权威来源,
除非绝大多数实现极有可能需要清理,否则不要要求它
我认为,在Stream
的例子中,这是"绝大多数可能性"。
最近再次思考这个问题后,我想分享另一个准则/经验法则,如果适用,可能会使决策更容易。
还没有人考虑的是用户/实现的比例:
- 如果用户多于实现,
特别是如果实现都在您的控制之下,请考虑将接口标记为
IDisposable
并强制用户始终释放,即使不是绝对必要的。只要这感觉适合手头的用例,它很可能会使用户的生活更简单:他们不需要检查是否需要处理,他们会系统地进行处理。缺点是您必须为那些不需要它的实现实现虚拟Dispose
方法。除非你有特殊的性能要求,否则这个调用是完全可以的(我想唯一的开销是虚拟表查找,方法本身可能被优化了(。如果实现多于用户,例如在插件系统的情况下,请考虑验证对象是否在核心中动态实现
IDisposable
,以便实现永远不必实现虚拟IDisposable
方法。缺点是核心中需要额外的逻辑。开销是强制转换和空检查的开销。
dr:我们经常假设用户多于实现。情况并非总是如此。
当然,这只是问题的一个维度,可以用来决定;另一个维度是需要处理的实现和不需要处理的实现之间的比率。 有关这方面的讨论,请参阅其他答案。
一旦你评估了每一个,立即考虑所有因素,用你的意见加盐,然后做出决定。