使用IDisposable资源时的屈服

12

在使用一次性资源时,是否有适当的方式进行迭代?返回的对象是 IDisposable 接口实现,但正在迭代的元素不是。

这里有一个示例:

public static IEnumerable<T> Fetch(IEnumerable<Guid> ids)
{
    using (var client = new CouchbaseClient())
    {
        yield return ids.Select(s => s.ToString());
    }
}

现在调用这个方法并不会释放获取到的using资源。我知道我可以使用ToList一次性返回所有内容,但还有没有更好的方法来"适当地"处理这个问题,或者说我必须手动跟踪IDisposable资源并在完成时手动释放它?


当迭代资源完成时,难道不是迭代资源的人负责处理吗? - Arthur Nunes
https://dev59.com/kEzSa4cB1Zd3GeqPiwOw - SLaks
@ArthurNunes 我怀疑是这样,但我只是好奇这种语言是否提供了任何解决方案,或者我是否必须手动完成(可能通过使包含类成为IDisposable并将处理逻辑移动到那里)。 - Jess
@SLaks 很有趣的问题;很高兴我看到了它,但不确定它与这个问题有多大关联。那里讨论了评论或子点吗?显然,标记的答案来自于一个 using,没有人指出它是不好的,但据我所知也没有人解释它为什么是可以的(或者为什么它是可以的)。 - Servy
你能修复一下你的代码让它可以编译通过吗?现在你正在返回一个集合,所以返回类型应该是 IEnumerable<IEnumerable<string>>。我猜这不是你想要的结果。 - svick
显示剩余2条评论
1个回答

7
简而言之,只要方法的调用者正确处理IEnumerator对象,您就不必担心它。 IEnumerator实现了IDisposable接口,并且在创建迭代器块时使用的逻辑实际上足够聪明,可以在其被处理时执行所有未执行的finally块。finally块是由using调用生成的,这是IDisposable资源被处理的地方。
因此,只要从此IEnumerable创建的IEnumerator对象是完全迭代(在这种情况下,最终的MoveNext调用将到达using块的末尾并处置资源)或处理IDisposable,客户端将被处理。
请注意,如果您担心代码的用户可能不正确处理IEnumerator对象,则最好不要使用具有延迟评估的迭代器块。如果要确保即使调用者不“友好”,也会急切地评估该方法(即采取您拥有的代码,将结果转储到列表中,然后返回该列表)。如果不处置资源的后果主要或全部与性能有关(不释放一些内存一段时间,保持打开连接等),则可能不是问题,但是如果永远保持锁定是一个主要问题(即如果不释放可能导致死锁的锁定资源),则懒惰评估的优点可能不值得。

你说得对,我误解了using关键字的使用方式。我没有意识到它会每次都打开,这并不是我想要的结果,但是它确实回答了我的问题。我将通过在需要时创建我的基类实例来解决这个问题。 - Jess
1
@DmitryStarosta 迭代器块直到.NET 2.0才被引入,因此该问题甚至不适用于早期版本。 - Servy
1
@AndrewKoester 它不会每次都打开它;当您请求第一个项时,它会打开一次,当您请求最后一个项或处置IEnumerator时,它会关闭。就像我说的,如果绝对必须处理资源,请不要这样做。如果通常最好,但是使用此代码的代码不友好并不会导致世界末日,则可以使用。 - Servy

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