为什么IEnumerator< T>继承自IDisposable,而非通用的IEnumerator没有?
-
04-07-2019 - |
题
我注意到泛型 IEnumerator< T>
继承自IDisposable,但非泛型接口IEnumerator却没有。为什么这样设计?
通常,我们使用foreach语句来浏览 IEnumerator< T>
实例。生成的foreach代码实际上有try-finally块,最终调用Dispose()。
解决方案
基本上这是一个疏忽。在C#1.0中, foreach
从不调用 Dispose
1 。使用C#1.2(在VS2003中引入 - 没有1.1,奇怪) foreach
开始检查 finally
块是否迭代器实现 IDisposable
- 他们必须这样做,因为回顾性地使 IEnumerator
扩展 IDisposable
会破坏每个人对 IEnumerator
的实现。如果他们认为 foreach
首先处理迭代器是有用的,我肯定 IEnumerator
会扩展 IDisposable
然而,当C#2.0和.NET 2.0出现时,他们有了一个新的机会 - 新的界面,新的继承。让接口扩展 IDisposable
更有意义,这样你就不需要在finally块中执行执行时检查,现在编译器知道如果迭代器是 IEnumerator&lt ; T>
它可以发出对 Dispose
的无条件调用。
编辑:在迭代结束时调用 Dispose
是非常有用的(但它结束了)。这意味着迭代器可以保留资源 - 这使得它可以逐行读取文件。迭代器块生成 Dispose
实现,这些实现确保任何 finally
块与“当前执行点”相关。迭代器在处理时执行 - 这样你就可以在迭代器中编写普通代码,并且应该适当地进行清理。
1 回顾1.0规范,已经指定了。我还没有能够验证这个早先的声明,即1.0实现没有调用 Dispose
。
其他提示
的IEnumerable< T>不继承IDisposable。的IEnumerator< T>然而,继承IDisposable,而非通用IEnumerator没有。即使将 foreach 用于非通用IEnumerable(返回IEnumerator),编译器仍会生成对IDisposable的检查,如果枚举器实现了接口,则调用Dispose()。
我猜通用的枚举器< T>继承自IDisposable所以不需要运行时类型检查—它可以继续调用Dispose(),它应该具有更好的性能,因为如果枚举器有一个空的Dispose,它可能会被优化掉(方法。
我知道这是一个古老的讨论,但我写了一个库,我在其中使用了T的T / IEnumerator,其中库的用户可以实现自定义迭代器,他们应该只实现T的IEnumerator。
我发现很奇怪,T的IEnumerator会从IDisposable继承。如果我们想要释放无人资源,我们实施IDisposable吗?因此,它只与实际拥有非托管资源的枚举器相关 - 如IO流等。为什么不让用户在其枚举器上实现T的IEnumerator和IDisposable是否有意义?在我的书中,这违反了单一责任原则 - 为什么要混合枚举器逻辑和处理对象。
IEnumerable`是否继承IDisposing?根据.NET反射器或 MSDN 。您确定不要将其与 IEnumerator 混淆吗?它使用IDisposing,因为它只用于枚举集合而不是长寿。
有点难以确定,除非你设法得到AndersH本人或与他亲近的人的回复。
然而,我的猜测是它与“收益率”有关。同时在C#中引入的关键字。如果你查看编译器在“yield return x”时生成的代码。使用,您将看到包含在实现IEnumerator的帮助器类中的方法;让IEnumerator从IDisposable下降,确保它在枚举完成时可以清理。
IEnumerable< T>
和 IEnumerable
的全部事情是 IEnumerable
早于.Net的模板内容的结果。我怀疑你的问题是一样的。