非泛型版本的IEnumerable是否支持延迟执行?

3

如果是这样,它支持哪些.NET Framework版本?

我已经在.NET Framework 4.0上进行了测试,它可以正常工作:

using System;
using System.Collections.Generic;

public class TestClass
{
    public IEnumerable Defer()
    {
        yield return 1;
        yield return 2;
        yield return 3;
    }
}

迭代器(yield关键字)最初在C# 2版本中推出,并随VS2005一起发布。 - Hans Passant
3个回答

4

是的,自从有了yield关键字以来就支持了。唯一的区别是它更多或少是IEnumerable<object>,如果必须进行装箱,则可能导致效率低下。除此之外,它完全相同。


1
延迟执行与“yield”无关,“yield”只是语法糖。使用“IEnumerable”在.NET 1.0中也可以实现延迟执行,这并没有什么不可行之处。 - leppie
真的。但是,OP的示例中特别使用了yield,这表明他的目标不是1.0。(现在还有人吗?) - Mike Caron
谢谢你提醒我yield只是语法糖。至于使用1.0,我同意。 - Edgar Gonzalez

1

由于yield关键字被降低到编译器技巧,因此这应该可以工作。它肯定适用于2.0运行时;但是对于1.1,我不敢发表任何声明。


1
非泛型的IEnumerable未实现IDisposable接口。在使用不支持IEnumerable(Of T)的枚举器时,VB.Net和C#可能会鸭子类型IDisposable或.Dispose()方法,但不能保证所有非泛型IEnumerable的消费者都这样做。如果可枚举对象的消费者没有正确地进行.Dispose()操作,则枚举器的执行,包括显式或隐式的finally子句,将被放弃。

我正在寻找更多关于这个问题的信息;我只是无法理解IEnumerable中这个明显的设计缺陷。我正在阅读这篇文章,https://www.codeproject.com/Articles/29534/IDisposable-What-Your-Mother-Never-Told-You-About,在“向后兼容性”下指出,期望非泛型IEnumerator的客户端代码没有正确处理迭代器的释放。这可能会导致什么样的错误? - TamaMcGlinn
@TamaMcGlinn:考虑一个在多个线程中使用的集合。如果枚举集合的所有代码都调用GetEnumerator,快速枚举集合,然后调用该枚举器上的Dispose,那么让GetEnumerator获取锁并让Dispose释放它可能会很好地工作。但是,如果Dispose从未被调用,则需要使用集合的代码可能会无限期地被阻塞。也许可以通过使集合持有一个引用来解决这个问题,该引用将标识活动的枚举器(如果存在),并且具有... - supercat
检查锁是否被枚举器持有,如果是,则开始等待一段时间以获取锁。如果失败,代码可以启用辅助锁(枚举器每次获取并释放元素),将其余集合枚举到列表中,告诉枚举器从该列表中获取剩余数据,然后窃取主锁。但这将添加很多复杂性,如果枚举器的用户遵守适当的规范,则不需要这种复杂性。 - supercat
明白了。这是否意味着让IEnumerator<T>继承自IEnumerator违反了Liskov替换原则?当接口指定接受所有IEnumerator时,你不能仅仅传递一个IEnumerator<T>给某人,因为这样的接口不会处理IEnumerator<T>的Dispose,这可能会导致锁定问题,正如你所描述的那样。 - TamaMcGlinn
@TamaMcGlinn:问题在于IEnumerable.GetEnumerator基本上可以返回三种不同的东西:“可以安全放弃但不实现IDisposable”的对象,“只有调用Dispose才能放弃的对象”,或者“可以安全放弃,无论是否调用Dispose”,而客户端代码没有好的方式来处理这三种情况。 IEnumerable<T>保证不会返回第一种类型的对象。 IEnumerable的原始设计者可能希望它永远不会返回第二种类型的对象,但这从未是现实。 - supercat
@TamaMcGlinn:在我看来,真正的问题是IEnumerableIEnumerable<T>以及IEnumeratorIEnumerator<T>都省略了所有实现都应该支持的特性和属性,例如“调用GetEnumerator是否会产生一个可以安全丢弃的对象”、“你知道你包含多少项”或“你总是报告相同的内容”。这应该通过使用属性来处理,而不是使用不同的接口,以便允许枚举器被其他枚举器包装而不会出现包装类型的爆炸。 - supercat

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接