IDisposable项的{Iterator函数

时间:2017-03-31 10:38:34

标签: c# iterator idisposable enumerator

假设我想创建一个产生IDisposable项的迭代器函数。

IEnumerable<Disposable> GetItems()
{
    yield return new Disposable();
    yield return new Disposable();
}

这似乎不适合客户端代码:

foreach (var item in source.GetItems())
{
    using (item)
    {
        // ...
    }
}

直观地说,using为时已晚。事情可能会被转移。有人可能会意外地在<{em> foreachusing之间插入代码。不理想。

我正在寻找更好的选择!

我想到的一种方法是创建以下API而不是迭代器函数:

// Client
while (source.HasItem)
{
    using (var item = source.GetNextItem())
    {
        // ...
    }
}

// Source
private IEnumerator<Disposable> Enumerator { get; }
private bool? _hasItem;
bool HasItem
{
    get
    {
        if (this._hasItem == null) this._hasItem = this.Enumerator.MoveNext();
        return this._hasItem;
    }
}
Disposable GetNextItem()
{
    if (!this.HasItem) throw new IndexOutOfBoundsException();
    this._hasItem = null;
    return this.Enumerator.Current;
}

但现在看来源必须成为IDisposable !怎么会知道何时处置Enumerator?这可能是一种令人不快的副作用。

我正在寻找一种在客户端感觉稳固的替代方案,但这也使源不会变为IDisposable

编辑 - 澄清:我忘了提到我们需要的一些内容来自迭代器。具体来说,假设我们正在返回IDbCommand个实例,IDisposable。在返回每个命令之前,我们需要使用一些查询数据填充它,而查询数据又来自一个简单的迭代器方法。

3 个答案:

答案 0 :(得分:4)

如果我理解正确,以下模式适合您

foreach (var item in source.GetItems())
{
    using (item)
    {
        // ...
    }
}

但潜在的问题是将一些代码放在using块之外。那你为什么不把这个逻辑包装在自定义扩展方法中呢?

public static class EnumerableExtennisons
{
    public static IEnumerable<T> WithUsing<T>(this IEnumerable<T> source)
        where T : IDisposable
    {
        foreach (var item in source)
        {
            using (item)
                yield return item;
        }
    }
}

通过这种方式,您可以确保将项目包装在using块*中,然后再将其返回给调用者,因此调用者无法在其之前/之后插入代码。 C#编译器生成的代码确保在item.Dispose的{​​{1}}或MoveNext方法中调用Dispose(如果迭代结束时更早)。

用法是在需要时附加IEnumerator<T>来代替.WithUsing()阻止:

using

答案 1 :(得分:1)

我们只能 @register.simple_tag def localdate(): lang = translation.get_language() fmt = DATE_FORMATS.get(lang, DATE_FORMATS[DEFAULT_LANG]) now = datetime.now() return datefilter(now, fmt) ,只支持IEnumerator<T>MoveNext()

现在,底层的私有迭代器函数可以是流式传输,负责处理项目。不会向客户端引入无效操作 - 除非他们尝试存储借用的对象并稍后尝试使用它们,否则很明显已经处理了这些对象。

Current

答案 2 :(得分:-1)

迭代器函数本身可以负责放置使用(即在请求下一个时处理当前项目)。

IEnumerable<Disposable> GetItems()
{
    // Assumming CreateItems() is an iterator as well
    foreach (var item in this.CreateItems())
        using (item) yield return item;
}

因此,我们将基本上拥有一个仅限流的迭代器。像ToList()这样的方法变得毫无意义,因为一次只能使用一个项目。

不幸的是,这确实会产生错误的空间。