2016-03-04 74 views
5

在返回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

我搜索了一下,發現沒有類似的問題,但我確定應該有一個 - 這是一個非常基本的問題。 – mafu

+5

'break;'應該取消它,生成的代碼應該處理枚舉器。如果來源不正確,那麼需要修正。 –

+0

@ DanielA.White我想取消它在調用範圍內,而不是枚舉方法本身 – mafu

回答

12

一般而言,這是實現IDisposableIEnumerator<>,如果你看看IEnumerator<>的定義,你會發現:

public interface IEnumerator<out T> : IDisposable, IEnumerator 

foreach語句中正確Dispose()IEnumerable<>接收IEnumerator<>,使即:

IEnumerable<SomeClass> res = SomeQuery(); 

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

在以任何方式離開foreach(該break,例外,自然整理res),IEnumerator<>Dispose()應該被調用。見https://msdn.microsoft.com/en-us/library/aa664754(v=vs.71).aspx爲的foreach是如何實現的一個例子(try ...... finally ......用Dispose()finally內)

注意,C#會產生「正確」的代碼爲yield函數內使用using。例如,見這裏:http://goo.gl/Igzmiz

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(); 
     } 
    } 
} 

IEnumerator<>Dispose()方法將調用m__Finally1方法將(IDisposable)this.<r>5__1.Dispose();(其中5__1rOpenResource()返回)。該m__Finally甚至被稱爲如果代碼簡單的「退出」的while (r.Read())

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

和/或是否有異常。

catch 
{ 
    this.System.IDisposable.Dispose();