2011-07-01 12 views
7

Quale sarebbe il modo più elegante per elaborare il primo elemento IEnumerable in modo diverso rispetto ad altri, senza dover eseguire il test su ogni iterazione?Il modo più elegante per elaborare prima IEnumerable item in modo diverso

Con un test su ogni iterazione, sarebbe simile a questa:

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

Se faccio questo:

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

che richiamerà GetEnumerator due volte, che vorrei evitare (per Casi Linq-to-Sql, ad esempio).

Come lo faresti, se hai bisogno di fare più volte il codice?

risposta

12

Se avevo bisogno di farlo in diversi luoghi, mi estraggo un metodo:

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

Chiamato come:

Process(items, ProcessDifferently, ProcessNormally); 

Ci sono anche altre opzioni, ma sarebbe davvero dipendere da la situazione.

+0

Grazie, pulito e pulito. Fa esattamente quello che ho chiesto. – dilbert

0

si può fare alla vecchia maniera:

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

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

Funzionerà per liste e array, ma ho bisogno di fare la stessa cosa per IEnumerable, senza iterarlo due volte. – dilbert

+0

Ah sì, hai ragione. Ho modificato l'esempio di codice per chiarezza. Vai con risposta ai skeets- molto bello. – HitLikeAHammer

1

Ecco un altro modo:

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

Questo uscite:

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

Nessun condizionali. : D

MODIFICA: "Testa" e "Coda" sarebbero probabilmente termini migliori ma sono troppo pigro per cambiarlo ora.

Problemi correlati