Mor*_*eng 72 c# generics ienumerator
我注意到泛型IEnumerator<T>继承自IDisposable,但非通用接口IEnumerator没有.为什么这样设计?
通常,我们使用foreach语句来遍历IEnumerator<T>实例.生成的foreach代码实际上有try-finally块,最终调用Dispose().
Jon*_*eet 80
基本上这是一个疏忽.在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<T>它可以发出无条件调用Dispose.
编辑:Dispose在迭代结束时被调用是非常有用的(但它结束了).这意味着迭代器可以保留资源 - 这使得它可以逐行读取文件.迭代器阻塞生成器Dispose实现,确保finally与迭代器的"当前执行点"相关的任何块在处理时执行 - 因此您可以在迭代器中编写正常代码并进行适当的清理.
1回顾1.0规范,已经指定了.我还没有能够验证这个1.0实现没有调用的早期声明Dispose.
IEnumerable <T>不继承IDisposable.IEnumerator <T>确实继承了IDisposable,而非通用IEnumerator却没有.即使将foreach用于非泛型IEnumerable(返回IEnumerator),编译器仍将生成对IDisposable的检查,并在枚举器实现接口时调用Dispose().
我猜通用的Enumerator <T>继承自IDisposable,所以不需要运行时类型检查 - 它可以继续调用Dispose(),它应该具有更好的性能,因为它可能会被优化掉,如果枚举器有一个空的Dispose()方法.
小智 5
我合理地编写了一个我使用的库IEnumerable of T/IEnumerator of T库的用户可以在其中实现他们应该实现的自定义迭代器IEnumerator of T。
我发现 T 的 IEnumerator 会从 IDisposable 继承非常奇怪。如果我们想释放非托管资源,我们实施 IDisposable 对吗?所以它只与实际持有非托管资源的枚举器相关 - 如 IO 流等。如果有意义,为什么不让用户在他们的枚举器上实现 T 的 IEnumerator 和 IDisposable 呢?在我的书中,这违反了单一职责原则 - 为什么混合枚举器逻辑和处理对象。