Si j'utilise:
var strings = new List<string> { "sample" };
foreach (string s in strings)
{
Console.WriteLine(s);
strings.Add(s + "!");
}
le Add
dans le foreach
jette une InvalidOperationException (la collection a été modifiée; l'opération d'énumération peut ne pas s'exécuter), ce que je considère comme logique, car nous tirons le tapis de sous nos pieds.
Cependant, si j'utilise:
var strings = new List<string> { "sample" };
strings.ForEach(s =>
{
Console.WriteLine(s);
strings.Add(s + "!");
});
il se tire rapidement dans le pied en faisant une boucle jusqu'à ce qu'il lance une OutOfMemoryException.
Cela me surprend, car j'ai toujours pensé que List.ForEach n'était qu'un wrapper pour foreach
ou pour for
.
Quelqu'un at-il une explication sur le comment et le pourquoi de ce comportement?
(Inspiré par la boucle ForEach pour une liste générique répétée à l'infini )
foreach
ou pourfor
." Il pourrait encore utiliserfor
. Vous pouvez effectuer la même action dans unefor
boucle et générer la même OutOfMemoryException en conséquence.Réponses:
C'est parce que la
ForEach
méthode n'utilise pas l'énumérateur, elle parcourt les éléments avec unefor
boucle:(code obtenu avec JustDecompile)
Puisque l'énumérateur n'est pas utilisé, il ne vérifie jamais si la liste a changé et la condition de fin de la
for
boucle n'est jamais atteinte car elle_size
est augmentée à chaque itération.la source
_size
calculé? Si c'est juste pré-calculé, si devrait être exécuté une fois pour mon exemple. Il est évidemment rafraîchi d'une manière ou d'une autre._version
variable privéeList<T>
qui pourrait détecter ce type de scénarios, car elle est mise à jour sur les opérations qui modifient la liste elle-même.List<T>.ForEach
est implémenté à l'for
intérieur, il n'utilise donc pas d'énumérateur et permet de modifier la collection.la source
Parce que le ForEach attaché à la classe List utilise en interne une boucle for qui est directement attachée à ses membres internes - que vous pouvez voir en téléchargeant le code source du framework .NET.
http://referencesource.microsoft.com/netframework.aspx
Où en tant que boucle foreach est avant tout une optimisation du compilateur mais doit également opérer contre la collection en tant qu'observateur - donc si la collection est modifiée, elle lève une exception.
la source
Add
ligne avecstrings.Insert(0, s + "!")
juste «échantillon» imprime. Il est étrange que cela ne soit pas du tout mentionné dans la documentation.Nous connaissons ce problème, c'était un oubli lors de sa rédaction initiale. Malheureusement, nous ne pouvons pas le changer car cela empêcherait maintenant ce code précédemment fonctionnel de s'exécuter:
L'utilité de cette méthode elle-même est discutable comme l'a souligné Eric Lippert , nous ne l'avons donc pas incluse pour les applications de style .NET pour Metro (c'est-à-dire les applications Windows 8).
David Kean (équipe BCL)
la source