214 votes

foreach vs someList.Foreach() {}

Il y a apparemment plusieurs façons pour itérer sur une collection. Curieux, s’il n’y a aucune différence, ni pourquoi vous utiliseriez une façon sur l’autre.

Premier type :

Autre manière :

Je suppose que sur le dessus de ma tête, qu’au lieu de délégué anonyme j’utilise ci-dessus, vous auriez un délégué réutilisable, vous pourriez spécifier...

163voto

Will Points 76760

Il est important et utile, la distinction entre les deux.

Parce que .ForEach utilise un for boucle pour parcourir la collection, c'est tout à fait valide:

someList.ForEach(x => if(x.RemoveMe) someList.Remove(x));

alors qu' foreach utilise un agent recenseur, ce n'est donc pas valide:

foreach(var item in someList)
  if(item.RemoveMe) someList.Remove(item);

tl;dr: Ne PAS copypaste ce code dans votre application!

Ces exemples ne sont pas les meilleures pratiques, ils sont juste pour démontrer les différences entre ForEach() et foreach.

Supprimer des éléments d'une liste dans un for boucle peut avoir des effets secondaires. Le plus commun est décrit dans les commentaires à cette question.

Généralement, si vous êtes à la recherche pour supprimer plusieurs éléments d'une liste, vous voulez séparer la détermination des éléments à supprimer de la suppression effective de l'. Il ne gardez pas votre code compact, mais elle garantit que vous ne manquez pas tous les éléments.

91voto

Anthony Points 2537

Nous avons eu un peu de code ici (dans VS2005 et C#2.0) où le précédent ingénieurs sont sortis de leur manière d'utiliser list.ForEach( delegate(item) { foo;}); au lieu de foreach(item in list) {foo; }; pour tout le code qu'ils ont écrit. par exemple, un bloc de code pour la lecture des lignes à partir d'un dataReader.

Je ne sais toujours pas exactement pourquoi ils ont fait cela.

Les inconvénients de l' list.ForEach() sont:

  • Il est plus prolixe en C# 2.0. Toutefois, en C# 3-delà, vous pouvez utiliser le "=>" syntaxe pour faire bien laconique expressions.

  • Il est moins familier. Les personnes qui ont à maintenir ce code me demande pourquoi vous l'avez fait de cette façon. Il m'a fallu un certain temps pour décider qu'il n'y avait aucune raison, sauf peut-être pour faire de l'écrivain judicieux (la qualité du reste du code miné). Il a également été moins lisible, avec le "})" à la fin du délégué bloc de code.

  • Voir aussi le projet de Loi de Wagner livre "Effective C#: 50 Moyens Spécifiques pour Améliorer Votre C#", où il parle de pourquoi foreach est préféré à d'autres boucles comme pour la ou les boucles "while" - le point principal est que vous êtes de laisser le compilateur décider de la meilleure façon de construire la boucle. Si une future version du compilateur gère à utiliser une technique plus rapide, vous obtiendrez gratuitement en utilisant foreach et de reconstruction, plutôt que de changer votre code.

  • un foreach(item in list) construire vous permet d'utiliser break ou continue si vous avez besoin de sortir de l'itération ou la boucle. Mais vous ne pouvez pas modifier la liste à l'intérieur d'une boucle foreach.

Je suis surpris de voir qu' list.ForEach est légèrement plus rapide. Mais ce n'est probablement pas une raison valable pour l'utiliser dans tout , que serait prématuré d'optimisation. Si votre application utilise une base de données ou un service web, pas de contrôle de la boucle, est presque toujours va être où le temps passe. Et avez-vous comparé contre une for boucle de trop? L' list.ForEach pourrait être plus rapide en raison de l'aide qu'à l'interne et à un for boucle sans l'emballage serait encore plus rapide.

Je suis en désaccord que l' list.ForEach(delegate) version est "la plus fonctionnelle" de manière significative. Il passe d'une fonction à une fonction, mais il n'y a pas de grande différence dans le résultat ou le programme de l'organisation.

Je ne pense pas qu' foreach(item in list) "dit exactement comment vous voulez qu'il fait" - for(int 1 = 0; i < count; i++) boucle n'est qu'un foreach boucle laisse le choix de contrôler jusqu'à le compilateur.

Mon sentiment est sur un nouveau projet, d'utiliser foreach(item in list) pour la plupart des boucles pour adhérer à l'usage commun et pour des raisons de lisibilité, et d'utiliser list.Foreach() seulement pour les blocs courts, quand vous pouvez faire quelque chose de plus élégamment ou de manière compacte avec le C# 3 "=>" de l'opérateur. Dans de tels cas, il peut y avoir une extension LINQ méthode qui est plus spécifique que l' ForEach(). Voir si Where(), Select(), Any(), All(), Max() ou une des nombreuses autres méthodes LINQ n'est pas déjà faire ce que vous voulez à partir de la boucle.

24voto

Mark Cidade Points 53945

sont légèrement plus rapide-it accès le tableau interne par index alors qu’un déclaration utilise un `` :

L’essai ci-dessus donne ces réponses, donner ou prendre quelques millisecondes :

Selon comment vous faire varier le test, les différences peuvent être plus grand ou plus petit, mais le `` est toujours plus rapide, même si négligeable donc.

20voto

plinth Points 26817

Pour le fun, j'ai sauté de la Liste de réflecteur et c'est le C#résultant:

public void ForEach(Action<T> action)
{
    if (action == null)
    {
        ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match);
    }
    for (int i = 0; i < this._size; i++)
    {
        action(this._items[i]);
    }
}

De même, la MoveNext dans Énumérateur qui est utilisé par la foreach est ceci:

public bool MoveNext()
{
    if (this.version != this.list._version)
    {
        ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumFailedVersion);
    }
    if (this.index < this.list._size)
    {
        this.current = this.list._items[this.index];
        this.index++;
        return true;
    }
    this.index = this.list._size + 1;
    this.current = default(T);
    return false;
}

La Liste.ForEach est beaucoup plus réduite que MoveNext - beaucoup moins de traitement sera le plus souvent JIT en quelque chose d'efficace.

En outre, foreach() va allouer un nouvel agent Recenseur n'importe quoi. La GC est un ami, mais si vous faites la même foreach à plusieurs reprises, cela va faire plus d'objets jetables, par opposition à la réutilisation de la même délégué - MAIS - c'est vraiment une frange de cas. En utilisation normale, vous verrez que peu ou pas de différence.

16voto

Daniel Earwicker Points 63298

Je sais que les deux obscur-ish choses qui les rendent différents. Aller moi!

Tout d'abord, il y a le classique bug de faire un délégué pour chaque élément de la liste. Si vous utilisez le mot-clé foreach, tous vos délégués peuvent se référant au dernier élément de la liste:

    // A list of actions to execute later
    List<Action> actions = new List<Action>();

    // Numbers 0 to 9
    List<int> numbers = Enumerable.Range(0, 10).ToList();

    // Store an action that prints each number (WRONG!)
    foreach (int number in numbers)
        actions.Add(() => Console.WriteLine(number));

    // Run the actions, we actually print 10 copies of "9"
    foreach (Action action in actions)
        action();

    // So try again
    actions.Clear();

    // Store an action that prints each number (RIGHT!)
    numbers.ForEach(number =>
        actions.Add(() => Console.WriteLine(number)));

    // Run the actions
    foreach (Action action in actions)
        action();

La Liste.ForEach méthode n'ont pas ce problème. L'élément actuel de l'itération est passé par valeur, comme un argument à l'extérieur lambda, et puis l'intérieur lambda correctement saisit cet argument dans sa propre fermeture. Le problème est résolu.

(Malheureusement, je crois ForEach est un membre de la Liste, plutôt qu'une extension de la méthode, mais il est facile de définir vous-même si vous disposez de cette installation sur toute énumérable type.)

Deuxièmement, l'approche de la méthode ForEach a une limitation. Si vous êtes à la mise en œuvre de IEnumerable en utilisant les taux de retour, vous ne pouvez pas faire un taux de retour à l'intérieur de la lambda. Donc à parcourir les éléments dans une collection pour obtenir le retour des choses n'est pas possible par cette méthode. Vous aurez à utiliser le mot-clé foreach et de travailler autour de la fermeture, le problème manuellement en faisant une copie de la boucle de courant de valeur à l'intérieur de la boucle.

Plus ici

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X