2011-07-01 3 views
7

Какой будет самый элегантный способ обработки первого элемента IEnumerable по-другому, чем другие, без необходимости тестировать на каждой итерации?Самый элегантный способ обработки первого элемента IEnumerable по-другому

С тестом на каждой итерации, она будет выглядеть следующим образом:

// "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); 
} 

Если я это сделать:

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

он будет вызывать GetEnumerator дважды, что я хотел бы избежать (для Например, случаи Linq-to-Sql).

Как вы это сделаете, если вам нужно сделать несколько раз вокруг своего кода?

ответ

12

Если мне нужно сделать это в нескольких местах, я бы извлечь метод:

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

Called как:

Process(items, ProcessDifferently, ProcessNormally); 

Есть и другие варианты тоже, но это будет действительно зависеть от ситуация.

+0

Спасибо, чистый и аккуратный. Я точно спрашиваю. – dilbert

0

Вы можете сделать это старинке:

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

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

Это будет работать для списков и массивов, но мне нужно сделать то же самое для IEnumerable, не повторяя его дважды. – dilbert

+0

Ах да, вы правы. Для ясности я модифицировал пример кода. Пойдите с skeets ответ - очень приятно. – HitLikeAHammer

1

Вот еще один способ:

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

Это выходы:

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

Нет Условные. : D

EDIT: «Голова» и «Хвост», вероятно, были бы лучшими условиями, но я слишком ленив, чтобы изменить его сейчас.

 Смежные вопросы

  • Нет связанных вопросов^_^