2014-04-01 23 views
7

假設我有一個IEnumerable<T>,我想取第一個元素並將其餘元素傳遞給其他代碼。我可以使用Take(n)獲得第一個n元素,但是如何在不導致枚舉重新啓動的情況下訪問其餘元素?我可以從枚舉中獲取前n個元素,然後仍然使用枚舉的其餘部分?

例如,假設我有一個方法ReadRecords,它接受CSV文件中的記錄爲IEnumerable<string>。現在假設在該方法中,我想讀取第一條記錄(標題),然後將其餘記錄傳遞給ReadDataRecords方法,該方法也需要IEnumerable<string>。就像這樣:

void ReadCsv(IEnumerable<string> records) 
{ 
    var headerRecord = records.Take(1); 
    var dataRecords = ??? 

    ReadDataRecords(dataRecords); 
} 

void ReadDataRecords(IEnumerable<string> records) 
{ 
    // ... 
} 

如果我準備重新開始計數,然後我可以使用dataRecords = records.Skip(1)。但是,我不想重新啓動它 - 事實上,它可能不可重新啓動。

那麼,有沒有什麼辦法來取第一個記錄,然後剩餘的記錄(除了通過讀取所有的值到一個新的集合,並重新枚舉它們)?

+2

如果你只需要跳過一個,那麼有什麼大不了的?只需再次枚舉並跳過一個? – Paparazzi

+0

如果您需要[批處理支持],MoreLinq中有很好的解決方案(http://stackoverflow.com/questions/13731796/create-batches-in-linq)。還有一些關於特殊套管第一/最後一個元素的問題。 –

+0

@Blam:如果枚舉是通過枚舉內存中的某些東西來創建的,例如枚舉列表中的項目,那麼是的。但是如果枚舉不能重新開始,或者執行起來非常昂貴呢?我不明白爲什麼應該有兩次枚舉,因爲我只想要一次獲取每個項目。 –

回答

13

這是一個有趣的問題,我想你可以使用這樣的解決辦法,而不是使用LINQ得到枚舉並使用它:

private void ReadCsv(IEnumerable<string> records) 
{ 
    var enumerator = records.GetEnumerator(); 
    enumerator.MoveNext(); 
    var headerRecord = enumerator.Current; 
    var dataRecords = GetRemainingRecords(enumerator); 
} 

public IEnumerable<string> GetRemainingRecords(IEnumerator<string> enumerator) 
{ 
    while (enumerator.MoveNext()) 
    { 
     if (enumerator.Current != null) 
      yield return enumerator.Current; 
    } 
} 

更新:根據您的評論這裏是更多的擴展這樣做的方式:

public static class CustomEnumerator 
{ 
    private static int _counter = 0; 
    private static IEnumerator enumerator; 
    public static IEnumerable<T> GetRecords<T>(this IEnumerable<T> source) 
    { 
     if (enumerator == null) enumerator = source.GetEnumerator(); 

     if (_counter == 0) 
     { 
      enumerator.MoveNext(); 
      _counter++; 
      yield return (T)enumerator.Current; 
     } 
     else 
     { 
      while (enumerator.MoveNext()) 
      { 
       yield return (T)enumerator.Current; 
      } 
      _counter = 0; 
      enumerator = null; 
     } 
    } 
} 

用法:

private static void ReadCsv(IEnumerable<string> records) 
{ 
    var headerRecord = records.GetRecords(); 
    var dataRecords = records.GetRecords(); 
} 
+0

@TyCobb感謝您的編輯。 –

+0

沒有問題。感謝提醒我,枚舉器仍然存在於所有可愛的語法之上,這些語法不斷被添加到框架中,並且仍然可以很好地利用。 +1 – TyCobb

+0

謝謝。我想我可以創建一個通用類來獲取IEnumerable 的剩餘部分。順便說一句,爲什麼循環中的空測試?似乎多餘... –

3

是的,使用IEnumerable中的IEnumerator,您可以在方法調用之間維護位置;

一個簡單的例子;

public class Program 
{ 
    static void Main(string[] args) 
    { 
     int[] arr = new [] {1, 2, 3}; 
     IEnumerator enumerator = arr.GetEnumerator(); 
     enumerator.MoveNext(); 
     Console.WriteLine(enumerator.Current); 
     DoRest(enumerator); 
    } 

    static void DoRest(IEnumerator enumerator) 
    { 
     while (enumerator.MoveNext()) 
      Console.WriteLine(enumerator.Current); 
    } 
} 
+0

謝謝,但我真的希望能夠調用一個採用IEnumerable的方法,以保留更好的API。對不起,我沒有說清楚。 –