2011-07-01 33 views
7

Cách nào là cách thanh lịch nhất để xử lý mục IEnumerable đầu tiên khác với các mục khác, mà không phải thử nghiệm trên mỗi lần lặp?Cách thanh lịch nhất để xử lý mục IEnumerable đầu tiên khác nhau

Với một thử nghiệm trên mỗi lần lặp, nó sẽ trông như thế này:

// "first item done" flag 
bool firstDone = false; 

// items is an IEnumerable<something> 
foreach (var item in items) 
{ 
    if (!firstDone) 
    { 
     // do this only once 
     ProcessDifferently(item); 
     firstDone = true; 
     continue; 
    } 

    ProcessNormally(item); 
} 

Nếu tôi làm điều này:

ProcessDifferently(items.First()); 
ProcessNormally(items.Skip(1)); // this calls `items.GetEnumerator` again 

nó sẽ gọi GetEnumerator hai lần, mà tôi muốn tránh (ví Ví dụ, các trường hợp LINQ-to-Sql).

Bạn sẽ làm như thế nào nếu bạn cần thực hiện nhiều lần xung quanh mã của mình?

Trả lời

12

Nếu tôi cần phải làm điều đó ở một số nơi, tôi muốn trích xuất một phương pháp:

public void Process<T>(IEnumerable<T> source, 
         Action<T> firstAction, 
         Action<T> remainderAction) 
{ 
    // TODO: Argument validation 
    using (var iterator = source.GetEnumerator()) 
    { 
     if (iterator.MoveNext()) 
     { 
      firstAction(iterator.Current); 
     } 
     while (iterator.MoveNext()) 
     { 
      remainderAction(iterator.Current); 
     } 
    } 
} 

gọi như:

Process(items, ProcessDifferently, ProcessNormally); 

Có những lựa chọn khác nữa, nhưng nó sẽ thực sự phụ thuộc vào tình huống.

+0

Cảm ơn, sạch sẽ và gọn gàng. Có chính xác những gì tôi hỏi. – dilbert

0

Bạn có thể làm điều đó theo cách cũ thời:

var itemsList = items.ToList(); 
ProcessDifferently(itemsList[0]); 

for(int i=1;i<itemsList.Count;i++) 
{ 
    ProcessNormally(itemsList[i]); 
} 
+0

Điều này sẽ làm việc cho danh sách và mảng, nhưng tôi cần phải làm điều tương tự cho IEnumerable, mà không cần lặp lại nó hai lần. – dilbert

+0

Ah vâng, bạn nói đúng. Tôi đã sửa đổi ví dụ mã cho rõ ràng. Đi với skeets trả lời- rất tốt đẹp. – HitLikeAHammer

1

Dưới đây là một cách khác:

private static void Main(string[] args) 
    { 
     var testdata = new[] { "a", "b", "c", "d", "e" }; 

     var action = FirstThenRest<string>(
      s => Console.WriteLine("First: " + s), 
      s => Console.WriteLine("Rest: " + s)); 

     foreach (var s in testdata) 
      action(s); 
    } 

    public static Action<T> FirstThenRest<T>(Action<T> first, Action<T> rest) 
    { 
     Action<T> closure = t => 
          { 
           first(t); 
           closure = rest; 
          }; 

     return t => closure(t); 
    } 

này kết quả đầu ra:

First: a 
Rest: b 
Rest: c 
Rest: d 
Rest: e 

Không điều kiện. : D

CHỈNH SỬA: "Đầu" và "Đuôi" có thể là cụm từ tốt hơn nhưng tôi quá lười để thay đổi ngay bây giờ.