在返回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
,如果他已完成迭代。同样,也可以使用某种取消令牌。但这两种方法对我来说都很麻烦。
答案 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;
}
以任何方式退出foreach
(break
,例外,自然完成res
),Dispose()
的{{1}}应该被调用。请参阅https://msdn.microsoft.com/en-us/library/aa664754(v=vs.71).aspx,了解IEnumerator<>
如何实施的示例(foreach
... try
... finally
内Dispose()
})
请注意,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();