为啥 Enumerable.Range 实现 IDisposable?
Posted
技术标签:
【中文标题】为啥 Enumerable.Range 实现 IDisposable?【英文标题】:Why does Enumerable.Range Implement IDisposable?为什么 Enumerable.Range 实现 IDisposable? 【发布时间】:2012-07-04 13:04:47 【问题描述】:只是想知道为什么Enumerable.Range
实现IDisposable
。
我明白为什么IEnumerator<T>
需要,但IEnumerable<T>
不需要。
(我在玩我的 .Memoise() 实现时发现了这一点,它有类似
的语句if (enumerable is IDisposable)
((IDisposable)enumerable).Dispose();
在它的“源代码完成”方法中,我出于好奇设置了一个断点,并由测试触发。)
【问题讨论】:
csharpindepth.com/articles/chapter6/… 【参考方案1】:Enumerable.Range
在其方法体中使用yield return
。 yield return
语句在编译器的魔力下生成实现IDisposable
的匿名类型,如下所示:
static IEnumerable<int> GetNumbers()
for (int i = 1; i < 10; i += 2)
yield return i;
编译后有一个匿名嵌套类是这样的:
[CompilerGenerated]
private sealed class <GetNumbers>d__0
: IEnumerable<int>, IEnumerable, IEnumerator<int>, IEnumerator, IDisposable
//the implementation
//note the interface is implemented explicitly
void IDisposable.Dispose()
所以结果is
和IDisposable
。在此示例中,Dispose
方法留空。我认为原因是没有什么需要处理的。如果你yield return
一个包含非托管资源的类型,你可能会得到不同的编译结果。 (不确定)
【讨论】:
编译器实现了同一个类的IEnumerable和IEnumerator?有趣的。我得考虑一下这行得通。 哦,我现在明白了。它使用其状态机来检测 GetEnumerator 是否已被多次调用,如果没有,则返回自身。 这是有道理的,但是当工厂的返回类型没有时,让工厂方法返回一个实现IDisposable
的类型似乎是一种反模式。通过yield return
生成的编译器生成的迭代器似乎是这样的类型,如果从未调用过GetEnumerator()
,则无需处理就放弃实际上是无害的,但是一旦调用了GetEnumerator()
,通常就需要处理。顺便说一句,在至少调用一次 GetEnumerable()
之后在迭代器上调用 Dispose
只会使第一个枚举器无效。
是否在定义为使其所有枚举器无效(或不无效)的迭代器上调用 Dispose
?无论哪种方式,第一个 GetEnumerable
调用都有不同的行为是出乎意料的。编译器过早优化?
(为了澄清@supercat,在编译器生成的IEnumerable
实现上调用Dispose
是从不必要的,除非你错误地处理了迭代器。)以上是关于为啥 Enumerable.Range 实现 IDisposable?的主要内容,如果未能解决你的问题,请参考以下文章
为啥重复 Enumerable 到 Observable 转换块