为什么 IEnumerator从 IDisposable 继承,而非泛型 IEnumerator 则不继承?
我注意到通用 IEnumerator
继承自 IDisposable,但非通用接口 IEnumerator 则不然。 为什么要这样设计呢?
通常,我们使用 foreach 语句来遍历 IEnumerator
实例。 foreach 的生成代码实际上有一个 try-finally 块,它在 finally 中调用 Dispose() 。
I noticed that the generic IEnumerator<T>
inherits from IDisposable, but the non-generic interface IEnumerator does not. Why is it designed in this way?
Usually, we use foreach statement to go through a IEnumerator<T>
instance. The generated code of foreach actually has try-finally block that invokes Dispose() in finally.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
基本上这是一个疏忽。 在 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
。Basically it was an oversight. In C# 1.0,
foreach
never calledDispose
1. With C# 1.2 (introduced in VS2003 - there's no 1.1, bizarrely)foreach
began to check in thefinally
block whether or not the iterator implementedIDisposable
- they had to do it that way, because retrospectively makingIEnumerator
extendIDisposable
would have broken everyone's implementation ofIEnumerator
. If they'd worked out that it's useful forforeach
to dispose of iterators in the first place, I'm sureIEnumerator
would have extendedIDisposable
.When C# 2.0 and .NET 2.0 came out, however, they had a fresh opportunity - new interface, new inheritance. It makes much more sense to have the interface extend
IDisposable
so that you don't need an execution-time check in the finally block, and now the compiler knows that if the iterator is anIEnumerator<T>
it can emit an unconditional call toDispose
.EDIT: It's incredibly useful for
Dispose
to be called at the end of iteration (however it ends). It means the iterator can hold on to resources - which makes it feasible for it to, say, read a file line by line. Iterator blocks generateDispose
implementations which make sure that anyfinally
blocks relevant to the "current point of execution" of the iterator are executed when it's disposed - so you can write normal code within the iterator and clean-up should happen appropriately.1 Looking back at the 1.0 spec, it was already specified. I haven't yet been able to verify this earlier statement that the 1.0 implementation didn't call
Dispose
.IEnumerable不继承 IDisposable。 IEnumerator然而,它确实继承了 IDisposable,而非泛型 IEnumerator 却没有。 即使当您对非泛型 IEnumerable(返回 IEnumerator)使用 foreach 时,编译器仍会生成对 IDisposable 的检查,并在枚举器实现该接口时调用 Dispose()。
我猜通用 Enumerator继承自 IDisposable,因此不需要运行时类型检查 - 它可以直接调用 Dispose(),这应该具有更好的性能,因为如果枚举器具有空的 Dispose() 方法,则可能会对其进行优化。
IEnumerable<T> doesn't inherit IDisposable. IEnumerator<T> does inherit IDisposable however, whereas the non-generic IEnumerator doesn't. Even when you use foreach for a non-generic IEnumerable (which returns IEnumerator), the compiler will still generate a check for IDisposable and call Dispose() if the enumerator implements the interface.
I guess the generic Enumerator<T> inherits from IDisposable so there doesn't need to be a runtime type-check—it can just go ahead and call Dispose() which should have better performance since it can be probably be optimized away if the enumerator has an empty Dispose() method.
我合理地编写了一个库,其中使用了
IEnumerable of T
/IEnumerator of T
,该库的用户可以实现自定义迭代器,他们应该只实现IEnumerator of T.
我觉得很奇怪,T 的 IEnumerator 继承自 IDisposable。 如果我们想释放非托管资源,我们就实现 IDisposable,对吧? 因此,它只与实际保存非托管资源的枚举器相关,例如 IO 流等。如果有意义的话,为什么不让用户在其枚举器上实现 T 的 IEnumerator 和 IDisposable 呢? 在我的书中,这违反了单一责任原则 - 为什么混合枚举器逻辑和处置对象。
I reasontly wrote a library where I used
IEnumerable of T
/IEnumerator of T
where users of the library could implement custom iterators they should just implementIEnumerator of T
.I found it very strange that IEnumerator of T would inherit from IDisposable. We implement IDisposable if we want to free unmanaged resources right? So it would only be relevant for enumerators that actually hold unmanaged resources - like an IO stream etc. Why not just let users implement both IEnumerator of T and IDisposable on their enumerator if it makes sense? In my book this violates the single responsibility principle - Why mix enumerator logic and disposing objects.
IEnumerable` 继承 IDispose 吗? 根据 .NET 反射器或 MSDN。 您确定没有将其与 IEnumerator 混淆吗? 它使用 IDispose,因为它仅用于枚举集合,并不意味着长寿。
Does IEnumerable` inherit IDisposing? According to the .NET reflector or MSDN. Are you sure you're not confusing it with IEnumerator? That uses IDisposing because it only for enumerating a collection and not meant for longevity.
很难确定这一点,除非你设法得到 AndersH 本人或与他关系密切的人的回应。
不过,我的猜测是,它与 C# 中同时引入的“yield”关键字有关。 如果您查看使用“yield return x”时编译器生成的代码,您将看到该方法包装在实现 IEnumerator 的帮助程序类中; 让 IEnumerator 继承自 IDisposable 确保它可以在枚举完成时进行清理。
A bit hard to be definitive on this, unless you manage to get a response from AndersH himself, or someone close to him.
However, my guess is that it relates to the "yield" keyword that was introduced in C# at the same time. If you look at the code generated by the compiler when "yield return x" is used, you'll see the method wrapped up in a helper class that implements IEnumerator; having IEnumerator descend from IDisposable ensures that it can clean up when enumeration is complete.
IIRC 拥有
IEnumerable
和IEnumerable
的全部内容是IEnumerable
早于 .Net 模板内容的结果。 我怀疑你的问题也是同样的。IIRC The whole thing about having
IEnumerable<T>
andIEnumerable
is a result ofIEnumerable
predating .Net's template stuff. I suspect that your question is in the same way.