如何取消IEnumerable?

时间:2016-03-04 13:08:22

标签: c# .net ienumerable

在返回IEnumerable<>的方法中,我打开并循环遍历资源(例如数据库行阅读器)。循环结束后,资源再次关闭。

但是,调用者可能决定不完成枚举。这使资源保持开放状态。

示例:

IEnumerable<Foo> Bar ()
{
    using (var r = OpenResource()) {
        while (r.Read ()) {
            yield return r;
        }
    }
}

// OK - this closes the resource again
foreach (var foo in Bar()) {
    Console.WriteLine (foo);
}

// Not OK - resource stays open!
Console.WriteLine (Bar().First());

我该如何解决这个问题?我可以轻松地取消枚举,即告诉它跳过循环的其余部分,或者将其丢弃(将清理代码放在Dispose中)吗?

我考虑过返回Func<Result, bool>,以便用户可以让它返回false,如果他已完成迭代。同样,也可以使用某种取消令牌。但这两种方法对我来说都很麻烦。

1 个答案:

答案 0 :(得分:12)

通常是IEnumerator<>实现IDisposable,如果你看一下IEnumerator<>的定义,你会看到:

public interface IEnumerator<out T> : IDisposable, IEnumerator

foreach语句正确Dispose()IEnumerator<>收到的IEnumerable<>,以便:

IEnumerable<SomeClass> res = SomeQuery();

foreach (SomeClass sc in res)
{
    if (something)
        break;
}

以任何方式退出foreachbreak,例外,自然完成res),Dispose()的{​​{1}}应该被调用。请参阅https://msdn.microsoft.com/en-us/library/aa664754(v=vs.71).aspx,了解IEnumerator<>如何实施的示例(foreach ... try ... finallyDispose() })

请注意,C#将为finally函数中使用的using生成“正确”代码。例如,请参见:http://goo.gl/Igzmiz

yield

转换为

public IEnumerable<Foo> Bar()
{
    using (var r = OpenResource()) 
    {
        while (r.Read ()) 
        {
            yield return new Foo();
        }
    }
}

void IDisposable.Dispose() { int num = this.<>1__state; if (num == -3 || num == 1) { try { } finally { this.<>m__Finally1(); } } } Dispose()方法会调用IEnumerator<>方法m__Finally1(其中(IDisposable)this.<r>5__1.Dispose();5__1从{{}返回的r 1}})。如果代码只是“退出”OpenResource()

,则甚至会调用m__Finally
while (r.Read ())

和/或如果有例外。

if (!this.<r>5__1.Read())
{
    this.<>m__Finally1();