[英]Why does IEnumerator<T> inherit from IDisposable while the non-generic IEnumerator does not?
I noticed that the generic IEnumerator<T>
inherits from IDisposable, but the non-generic interface IEnumerator does not.我注意到通用IEnumerator<T>
继承自 IDisposable,但非通用接口 IEnumerator 没有。 Why is it designed in this way?为什么要这样设计?
Usually, we use foreach statement to go through a IEnumerator<T>
instance.通常,我们使用 foreach 语句遍历IEnumerator<T>
实例。 The generated code of foreach actually has try-finally block that invokes Dispose() in finally. foreach 生成的代码实际上有 try-finally 块,它在 finally 中调用 Dispose()。
Basically it was an oversight.基本上这是一个疏忽。 In C# 1.0, foreach
never called Dispose
1 .在 C# 1.0 中, foreach
从不调用Dispose
1 。 With C# 1.2 (introduced in VS2003 - there's no 1.1, bizarrely) foreach
began to check in the finally
block whether or not the iterator implemented IDisposable
- they had to do it that way, because retrospectively making IEnumerator
extend IDisposable
would have broken everyone's implementation of IEnumerator
.使用 C# 1.2(在 VS2003 中引入 - 奇怪的是没有 1.1) foreach
开始在finally
块中检查迭代器是否实现了IDisposable
- 他们必须这样做,因为回顾性地使IEnumerator
扩展IDisposable
会破坏每个人的实现IEnumerator
。 If they'd worked out that it's useful for foreach
to dispose of iterators in the first place, I'm sure IEnumerator
would have extended IDisposable
.如果他们发现foreach
首先处理迭代器很有用,我敢肯定IEnumerator
会扩展IDisposable
。
When C# 2.0 and .NET 2.0 came out, however, they had a fresh opportunity - new interface, new inheritance.然而,当 C# 2.0 和 .NET 2.0 出现时,他们有了一个新的机会——新的接口、新的继承。 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 an IEnumerator<T>
it can emit an unconditional call to Dispose
.让接口扩展IDisposable
更有意义,这样您就不需要在 finally 块中进行执行时检查,现在编译器知道如果迭代器是IEnumerator<T>
它可以发出无条件调用Dispose
。
EDIT: It's incredibly useful for Dispose
to be called at the end of iteration (however it ends).编辑:在迭代结束时调用Dispose
非常有用(但是它结束了)。 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 generate Dispose
implementations which make sure that any finally
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.迭代器块生成Dispose
实现,它确保与迭代器的“当前执行点”相关的任何finally
块在它被释放时都会被执行——这样你就可以在迭代器中编写正常的代码,并且应该适当地进行清理。
1 Looking back at the 1.0 spec, it was already specified. 1回顾 1.0 规范,它已经被指定了。 I haven't yet been able to verify this earlier statement that the 1.0 implementation didn't call Dispose
.我还没有能够验证之前的声明,即 1.0 实现没有调用Dispose
。
IEnumerable<T> doesn't inherit IDisposable. IEnumerable<T> 不继承 IDisposable。 IEnumerator<T> does inherit IDisposable however, whereas the non-generic IEnumerator doesn't.然而,IEnumerator<T> 确实继承了 IDisposable,而非泛型 IEnumerator 则没有。 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.即使您将foreach用于非泛型 IEnumerable(返回 IEnumerator),编译器仍会生成对 IDisposable 的检查,并在枚举器实现接口时调用 Dispose()。
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.我猜通用 Enumerator<T> 继承自 IDisposable,因此不需要进行运行时类型检查——它可以继续调用应该具有更好性能的 Dispose(),因为如果枚举器有一个空的 Dispose() 方法。
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 implement IEnumerator of T
.我合理地编写了一个库,其中我使用IEnumerable of T
IEnumerator of T
/ IEnumerable of T
IEnumerator of T
库的用户可以在其中实现自定义迭代器,他们应该只实现IEnumerator of T
。
I found it very strange that IEnumerator of T would inherit from IDisposable.我发现 T 的 IEnumerator 会从 IDisposable 继承非常奇怪。 We implement IDisposable if we want to free unmanaged resources right?如果我们想释放非托管资源,我们实施 IDisposable 对吗? 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?所以它只与实际持有非托管资源的枚举器相关——比如 IO 流等。如果有意义,为什么不让用户在他们的枚举器上实现 T 的 IEnumerator 和 IDisposable 呢? In my book this violates the single responsibility principle - Why mix enumerator logic and disposing objects.在我的书中,这违反了单一职责原则 - 为什么混合枚举器逻辑和处理对象。
Does IEnumerable` inherit IDisposing? IEnumerable` 是否继承了 IDisposing? According to the .NET reflector or MSDN .根据 .NET 反射器或MSDN 。 Are you sure you're not confusing it with IEnumerator ?你确定你没有将它与IEnumerator混淆吗? That uses IDisposing because it only for enumerating a collection and not meant for longevity.使用 IDisposing 是因为它仅用于枚举集合而不是为了长寿。
A bit hard to be definitive on this, unless you manage to get a response from AndersH himself, or someone close to him.对此有点难以确定,除非您设法得到 AndersH 本人或与他关系密切的人的回应。
However, my guess is that it relates to the "yield" keyword that was introduced in C# at the same time.但是,我的猜测是它与同时在 C# 中引入的“yield”关键字有关。 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;如果您查看使用“yield return x”时编译器生成的代码,您会看到该方法包含在实现 IEnumerator 的辅助类中; having IEnumerator descend from IDisposable ensures that it can clean up when enumeration is complete.让 IEnumerator 从 IDisposable 下降确保它可以在枚举完成时进行清理。
IIRC The whole thing about having IEnumerable<T>
and IEnumerable
is a result of IEnumerable
predating .Net's template stuff. IIRC 关于拥有IEnumerable<T>
和IEnumerable
的整个事情是IEnumerable
早于 .Net 的模板内容的结果。 I suspect that your question is in the same way.我怀疑你的问题也是如此。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.