我注意到泛型IEnumerator<T>
继承自 IDisposable,但非泛型接口 IEnumerator 没有。为什么要这样设计?
通常,我们使用 foreach 语句来遍历一个IEnumerator<T>
实例。foreach 生成的代码实际上有 try-finally 块,它在 finally 中调用 Dispose()。
我注意到泛型IEnumerator<T>
继承自 IDisposable,但非泛型接口 IEnumerator 没有。为什么要这样设计?
通常,我们使用 foreach 语句来遍历一个IEnumerator<T>
实例。foreach 生成的代码实际上有 try-finally 块,它在 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 块中进行执行时检查,现在编译器知道如果迭代器是 anIEnumerator<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() 方法。
我理性地编写了一个库,我在其中使用IEnumerable of T
/IEnumerator of T
该库的用户可以实现他们应该实现的自定义迭代器IEnumerator of T
。
我发现 T 的 IEnumerator 会从 IDisposable 继承,这很奇怪。如果我们想释放非托管资源,我们实现 IDisposable 对吗?所以它只与实际持有非托管资源的枚举器相关——比如 IO 流等。如果有意义的话,为什么不让用户在他们的枚举器上同时实现 T 的 IEnumerator 和 IDisposable 呢?在我的书中,这违反了单一责任原则——为什么要混合枚举器逻辑和处置对象。
IEnumerable` 是否继承 IDisposing?根据 .NET 反射器或MSDN。您确定不会将其与IEnumerator混淆吗?这使用 IDisposing 因为它仅用于枚举集合而不是为了长寿。
对此有点难以确定,除非您设法从 AndersH 本人或与他关系密切的人那里得到回应。
但是,我的猜测是它与同时在 C# 中引入的“yield”关键字有关。如果您查看使用“yield return x”时编译器生成的代码,您会看到该方法封装在一个实现 IEnumerator 的辅助类中;让 IEnumerator 从 IDisposable 下降确保它可以在枚举完成时进行清理。
IIRC 关于拥有IEnumerable<T>
并且IEnumerable
是IEnumerable
早于 .Net 模板内容的结果。我怀疑你的问题是一样的。