为啥 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 returnyield 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()  

所以结果isIDisposable。在此示例中,Dispose 方法留空。我认为原因是没有什么需要处理的。如果你yield return 一个包含非托管资源的类型,你可能会得到不同的编译结果。 (不确定)

【讨论】:

编译器实现了同一个类的IEnumerable和IEnumerator?有趣的。我得考虑一下这行得通。 哦,我现在明白了。它使用其状态机来检测 GetEnumerator 是否已被多次调用,如果没有,则返回自身。 这是有道理的,但是当工厂的返回类型没有时,让工厂方法返回一个实现 IDisposable 的类型似乎是一种反模式。通过yield return 生成的编译器生成的迭代器似乎是这样的类型,如果从未调用过GetEnumerator(),则无需处理就放弃实际上是无害的,但是一旦调用了GetEnumerator(),通常就需要处理。顺便说一句,在至少调用一次 GetEnumerable() 之后在迭代器上调用 Dispose 只会使第一个枚举器无效。 是否在定义为使其所有枚举器无效(或不无效)的迭代器上调用 Dispose?无论哪种方式,第一个 GetEnumerable 调用都有不同的行为是出乎意料的。编译器过早优化? (为了澄清@supercat,在编译器生成的IEnumerable 实现上调用Dispose从不必要的,除非你错误地处理了迭代器。)

以上是关于为啥 Enumerable.Range 实现 IDisposable?的主要内容,如果未能解决你的问题,请参考以下文章

为啥重复 Enumerable 到 Observable 转换块

扩展 Enumerable.Range [重复]

Enumerable.Range 的高内存消耗?

使用 Enumerable.Range() 填充字典的问题

关于 Enumerable.Range 与传统 for 循环的 foreach 的思考

从 Enumerable.Range 或 List<int> 填充 List<dynamic>