Domanda

ho un Enumerable<T> e sto cercando un metodo che mi permetta di eseguire un'azione per ciascun elemento, in un certo senso Select ma poi per gli effetti collaterali.Qualcosa di simile a:

string[] Names = ...;
Names.each(s => Console.Writeline(s));

O

Names.each(s => GenHTMLOutput(s));   
// (where GenHTMLOutput cannot for some reason receive the enumerable itself as a parameter)

Ci ho provato Select(s=> { Console.WriteLine(s); return s; }), ma non stampava nulla.

È stato utile?

Soluzione

Un modo rapido-e-facile per ottenere questo è:

Names.ToList().ForEach(e => ...);

Altri suggerimenti

Stai cercando il ForEach sempre sfuggente che attualmente esiste solo sulla raccolta generica List. Ci sono molte discussioni online sul fatto che Microsoft dovrebbe o non dovrebbe aggiungere questo come un metodo di LINQ. Attualmente, si deve rotolare il proprio:

public static void ForEach<T>(this IEnumerable<T> value, Action<T> action)
{
  foreach (T item in value)
  {
    action(item);
  }
}

Mentre il metodo All() fornisce abilità simili, è caso d'uso è per l'esecuzione di un test predicato su ogni articolo, piuttosto che un'azione. Naturalmente, può essere convinto a svolgere altri compiti, ma questo cambia un po 'la semantica e renderebbe più difficile per gli altri a interpretare il codice (vale a dire è questo uso del All() per un test predicato o un'azione?).

Disclaimer: Questo post non assomiglia più la mia risposta originale, ma piuttosto incorpora il circa sette anni di esperienza che ho guadagnato da allora. Ho fatto la modifica, perché questa è una questione altamente vista e nessuna delle risposte esistenti realmente coperto tutti gli angoli. Se volete vedere la mia risposta originale, è disponibile nella cronologia di revisione per questo post .


La prima cosa da capire qui è C # LINQ operazioni come Select(), All(), Where(), ecc, hanno le loro radici nel programmazione funzionale. L'idea era di portare alcune delle parti più utili e accessibile di programmazione funzionale al mondo .Net. Questo è importante, perché una parte fondamentale della programmazione funzionale è per le operazioni sono esenti da effetti collaterali. E 'difficile sottovalutare questo. Tuttavia, nel caso di ForEach() / each(), effetti collaterali sono tutta puprose dell'operazione. Aggiunta each() o ForEach() non è solo fuori dell'ambito programmazione funzionale degli altri operatori LINQ, ma in opposizione direttamente.

Ma capisco questo si sente insoddisfacente. Hai un problema reale è necessario risolvere. Perché tutta questa filosofia torre d'avorio dovrebbe ottenere nel senso di qualcosa che potrebbe essere veramente utile?

Eric Lippert, che era sul C # team di progettazione, al momento, ci può dare una mano qui. Si consiglia di utilizzare un ciclo foreach tradizionale:

  

[foreach ()] aggiunge nulla nuovo potere di rappresentanza al linguaggio. Facendo questo modo è possibile riscrivere il codice perfettamente chiaro:

     

foreach(Foo foo in foos){ statement involving foo; }

     

in questo codice:

     

foos.ForEach(foo=>{ statement involving foo; });

Il suo punto è, quando si guarda da vicino le opzioni di sintassi, non si guadagna nulla di nuovo da un interno ForEach() rispetto a un ciclo foreach tradizionale. Ho parzialmente d'accordo. Immaginate di avere in questo modo:

 foreach(var item in Some.Long(and => possibly)
                         .Complicated(set => ofLINQ)
                         .Expression(to => evaluate)
 {
     // now do something
 } 

Questo codice offusca significato, perché separa la parola foreach dalle operazioni del ciclo. Inoltre elenca il comando iterativo prima alle operazioni che definiscono la sequenza su cui opera il ciclo. Ci si sente molto più naturale a voler avere tali operazioni vengono prima, e poi avere il comando del ciclo al end della definizione della query. Inoltre, il codice è solo brutto. Sembra che sarebbe molto più bello di essere in grado di scrivere questo:

Some.Long(and => possibly)
   .Complicated(set => ofLINQ)
   .Expression(to => evaluate)
   .ForEach(item => 
{
    // now do something
});

Tuttavia, anche qui, alla fine ho venuto intorno a punto di vista di Eric. Mi sono reso conto di codice come si vede sopra è chiamata fuori per una variabile aggiuntiva. Se si dispone di una serie complicata di espressioni LINQ del genere, è possibile aggiungere informazioni preziose per il codice per la prima assegnazione dei risultato dell'espressione LINQ per una nuova variabile:

var queryForSomeThing = Some.Long(and => possibly)
                        .Complicated(set => ofLINQ)
                        .Expressions(to => evaluate);
foreach(var item in queryForSomeThing)
{
    // now do something
}

Questo codice si sente più naturale. Si mette la parola chiave foreach di nuovo accanto al resto del ciclo, e dopo la definizione della query. La maggior parte di tutti, il nome della variabile può aggiungere nuove informazioni che saranno utili per i futuri programmatori che cercano di comprendere lo scopo della query LINQ. Ancora una volta, vediamo l'operatore ForEach() desiderato davvero aggiunto nessuna nuova forza espressiva al linguaggio.

Tuttavia, ci mancano ancora due caratteristiche di un metodo di estensione ForEach() ipotetica:

  1. Non è componibile. Non è possibile aggiungere un ulteriore .Where() o GroupBy() o OrderBy() dopo una linea ciclo foreach con il resto del codice, senza creare una nuova dichiarazione.
  2. Non è pigro. Queste operazioni avvengono immediatamente. Non è così albasso me, per esempio, avere una forma in cui un utente sceglie un'operazione di un campo in uno schermo più grande che non è agito sul finché l'utente preme un pulsante di comando. Questa forma potrebbe consentire all'utente di cambiare idea prima di eseguire il comando. Questo è perfettamente normale ( semplice , anche) con una query LINQ, ma non è così semplice con un foreach.

Si potrebbe, naturalmente, fare il proprio metodo di estensione ForEach(). Diverse altre risposte hanno già implementazioni di questo metodo; non è tutto così complicato. Tuttavia, mi sento come se fosse inutile. C'è già un metodo esistente che si adatta ciò che vogliamo fare da entrambi i punti di vista semantico e operativi. Entrambe le caratteristiche mancanti di cui sopra possono essere risolti mediante l'uso dell'operatore Select() esistente.

Select() si adatta al tipo di trasformazione o proiezione descritto da entrambi gli esempi precedenti. Tenete a mente, però, che vorrei ancora non creare effetti collaterali. La chiamata a Select() deve restituire sia nuovi oggetti o proiezioni dagli originali. Questo a volte può essere aiutata attraverso l'uso di un tipo anonimo o un oggetto dinamico (se e solo se necessario). Se avete bisogno di risultati a persistere, per esempio, una variabile di lista originale, si può sempre chiamare .ToList() e assegnarlo al tuo variabile originale. Io aggiungo qui che io preferisco lavorare con le variabili IEnumerable<T> il più possibile sopra i tipi più concreti.

myList = myList.Select(item => new SomeType(item.value1, item.value2 *4)).ToList();

In sintesi:

  1. Basta attaccare con foreach maggior parte del tempo.
  2. Quando foreach davvero non farà (che probabilmente non è così spesso come si pensa), l'uso Select()
  3. Quando è necessario utilizzare Select(), è ancora possibile evitare in generale (programma-visibile) effetti collaterali, forse proiettando ad un tipo anonimo.

Purtroppo non c'è modo integrato per effettuare questa operazione nella versione corrente di LINQ. Il team quadro trascurato di aggiungere un metodo di estensione .ForEach. C'è una buona discussione su questo corso in questo momento sul seguente blog.

http://blogs.msdn.com/kirillosenkov/ archive / 2009/01/31 / foreach.aspx

E 'piuttosto facile aggiungere un però.

public static void ForEach<T>(this IEnumerable<T> enumerable, Action<T> action) {
  foreach ( var cur in enumerable ) {
    action(cur);
  }
}

Non si può fare questo subito con LINQ e IEnumerable - è necessario o implementare il proprio metodo di estensione, o lanciare la vostra enumerazione per un array con LINQ e quindi chiamare Array.ForEach ():

Array.ForEach(MyCollection.ToArray(), x => x.YourMethod());

Si prega di notare che a causa dei tipi di valore strada e le strutture di lavoro, se la raccolta è di un tipo di valore e modificare gli elementi della collezione in questo modo, non avrà alcun effetto sugli elementi della collezione originale.

A causa LINQ è stato progettato per essere una caratteristica di query e non una funzione di aggiornamento che non troverete un interno che esegue i metodi su IEnumerable , perché questo avrebbe permesso di eseguire un metodo (potenzialmente con effetti collaterali). In questo caso si può anche solo bastone con

  

foreach (string name in Names)
  Console.WriteLine (nome);

Bene, puoi anche usare lo standard foreach parola chiave, basta formattarla in un oneliner:

foreach(var n in Names.Where(blahblah)) DoStuff(n);

Scusa, pensavo che questa opzione meritasse di essere qui :)

C'è un metodo ForEach fuori della lista. Si potrebbe convertire il Enumerable to List chiamando il metodo ToList (), e quindi chiamare il metodo ForEach fuori di questo.

In alternativa, ho sentito parlare di persone che definiscono il proprio metodo ForEach off di IEnumerable. Ciò può essere realizzato essenzialmente chiamando il metodo ForEach, ma invece avvolgendolo in un metodo di estensione:

public static class IEnumerableExtensions
{
    public static IEnumerable<T> ForEach<T>(this IEnumerable<T> _this, Action<T> del)
    {
        List<T> list = _this.ToList();
        list.ForEach(del);
        return list;
    }
}

Utilizzando Parallel LINQ:

Names.AsParallel (). PerTutto (name => ...)

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top