2010-03-12 4 views
10

Souvent, lorsque j'ai affaire à des séquences LINQ, je veux envoyer chaque élément à une méthode retournant void, en évitant une boucle foreach. Cependant, je n'ai pas trouvé une manière élégante de faire ceci. Aujourd'hui, je l'ai écrit le code suivant:Envoyer des éléments dans une séquence LINQ à une méthode qui renvoie void

private StreamWriter _sw; 
    private void streamToFile(List<ErrorEntry> errors) 
    { 
     if (_sw == null) 
     { 
      _sw = new StreamWriter(Path.Combine 
            (Path.GetDirectoryName(_targetDatabasePath), "errors.txt")); 
     } 

     Func<ErrorEntry, bool> writeSelector = 
      (e) => { _sw.WriteLine(getTabDelimititedLine(e)); return true; }; 

     errors.Select(writeSelector); 

     _sw.Flush(); 
    } 

Comme vous pouvez le voir, j'écrire une fonction lambda qui retourne juste vrai, et je me rends compte que la méthode Select retourne une séquence de booleans- Je vais simplement ignorer que séquence. Cependant, cela semble un peu noobish et jank. Y a-t-il une manière élégante de faire ceci? Ou suis-je juste en train de mal appliquer LINQ?

Merci.

Répondre

13

Tout d'abord, votre code actuel ne fonctionnera pas.
Select, et la plupart des autres méthodes LINQ, utilisent l'exécution différée, ce qui signifie qu'ils ne font rien réellement jusqu'à ce que vous énumériez les résultats.

En général, vous ne devriez jamais utiliser un lambda avec des effets secondaires dans une requête LINQ.

Pour répondre à votre question, vous devez utiliser une boucle foreach.

Vous recherchez une méthode d'extension ForEach; Eric Lippert explains why Microsoft didn't write one.

Si vous voulez vraiment, vous pouvez écrire vous-même:

public static void ForEach<T>(this IEnumerable<T> sequence, Action<T> action) { 
    if (sequence == null) throw new ArgumentNullException("sequence"); 
    if (action == null) throw new ArgumentNullException("action"); 
    foreach(T item in sequence) 
     action(item); 
} 

//Return false to stop the loop 
public static void ForEach<T>(this IEnumerable<T> sequence, Func<T, bool> action) { 
    if (sequence == null) throw new ArgumentNullException("sequence"); 
    if (action == null) throw new ArgumentNullException("action"); 

    foreach(T item in sequence) 
     if (!action(item)) 
      return; 
} 
+0

Merci pour la réponse. J'ai quelques questions: Disons, pour une raison folle, j'ai appelé la méthode Count() sur la séquence de booléens que la méthode Select() renvoie. Cela forcerait l'énumération de la séquence et le code devrait fonctionner. Ma question est alors: pourquoi devrions-nous jamais utiliser lambdas avec des effets secondaires dans une requête LINQ? [EDIT: Merci pour le lien! Je vais vérifier cela.] –

+0

Correct; appeler 'Count()' forcera la séquence à être énumérée. – SLaks

+1

Vous ne devez jamais mélanger des effets secondaires avec LINQ car vos lambdas peuvent être exécutés plusieurs fois. Il peut être très difficile de savoir exactement combien de fois un lambda sera exécuté; vos lambdas devraient être idempotents. – SLaks

4

Le consensus commun est que LINQ est pour effectuer des requêtes et la sélection ... pendant que vous utilisez des méthodes itératives traditionnelles pour le bouclage et l'itération. Je déteste dire ceci mais vous utiliseriez une boucle foreach traditionnelle parce que vous voulez que vos requêtes linq soient exécutées, et vous itérez sur l'IEnumerable qui en résulte. Cela aide à la lisibilité du code, et je reconnais que LINQ est addictif. Vous voulez tout faire en utilisant Lambda et l'exécution différée, mais la boucle devrait être laissée à vos méthodes traditionnelles de bouclage C#. Cela va certainement aider avec des effets secondaires.

Questions connexes