Est-ce que foreach () itère par référence?

Considère ceci:

List obj_list = get_the_list(); foreach( MyClass obj in obj_list ) { obj.property = 42; } 

Est-ce que ‘obj’ est une référence à l’object correspondant dans la liste, de sorte que lorsque je modifie la propriété, la modification persiste dans l’instance de l’object une fois construite quelque part?

Oui, obj est une référence à l’object actuel de la collection (en supposant que MyClass est en fait une classe). Si vous modifiez des propriétés via la référence, vous modifiez l’object, comme prévu.

Sachez toutefois que vous ne pouvez pas modifier la variable obj elle-même, car c’est la variable d’itération. Vous obtiendrez une erreur de compilation si vous essayez. Cela signifie que vous ne pouvez pas l’annuler et que si vous itérez des types de valeur, vous ne pouvez modifier aucun membre car cela modifierait la valeur.

La spécification du langage C # indique (8.8.4)

“La variable d’itération correspond à une variable locale en lecture seule avec une scope qui s’étend sur l’instruction incorporée.”

Oui, jusqu’à ce que vous changiez le type générique de Liste en IEnumerable.

Vous avez posé 2 questions différentes ici, laissez-les dans l’ordre.

Une boucle foreach effectue-t-elle une itération par référence?

Si vous voulez dire dans le même sens que C ++ pour la boucle par référence, alors non. C # n’a pas de références de variables locales au même sens que C ++ et ne prend donc pas en charge ce type d’itération.

Le changement sera-t-il persistant

En supposant que MyClass est un type de référence, la réponse est oui. Une classe est un type de référence dans .Net et, par conséquent, la variable d’itération est une référence à la variable et non à une copie. Cela ne serait pas vrai pour un type de valeur.

Eh bien, il m’est arrivé que mes modifications n’étaient pas mises à jour dans une boucle foreach lorsque j’ai parcouru var collection:

 var players = this.GetAllPlayers(); foreach (Player player in players) { player.Position = 1; } 

Lorsque j’ai modifié var pour List, il a commencé à fonctionner.

Vous pouvez dans cette instance (en utilisant une List ) mais si vous deviez itérer sur le générique IEnumerable cela devient dépendant de son implémentation.

Si c’était encore une List ou T[] par exemple, tout fonctionnerait comme prévu. Le grand jeu vient quand vous travaillez avec un IEnumerable qui a été construit en utilisant le rendement. Dans ce cas, vous ne pouvez plus modifier les propriétés de T dans une itération et attendez-vous à ce qu’elles soient présentes si vous répétez le même IEnumerable .

Cela est vrai tant que ce n’est pas une structure.

Eh bien, sans comprendre exactement ce que vous entendez par “itérer par référence”, je ne peux pas répondre spécifiquement oui ou non, mais je peux dire que ce qui se passe sous la surface est que le framework .net construit une classe “énumérateur” pour chaque fois que le code client appelle un foreach, pour la durée de vie de foreach, qui maintient un pointeur de référence dans la collection en cours d’itération, et chaque fois que votre foreach est itéré, ir “délivre” un élément et énumérateur à l’élément suivant …

Cela se produit, que les éléments de la collection que vous parcourez soient des types de valeurs ou des types de référence.

obj est une référence à un élément dans la liste, donc si vous modifiez sa valeur, elle persistera. Maintenant, ce qui devrait vous préoccuper, c’est de savoir si get_the_list (); fait une copie complète de la liste ou renvoie la même instance.

Oui, c’est aussi la raison pour laquelle vous ne pouvez pas modifier l’object énumérable dans le contexte de l’instruction foreach.

Il est peut-être intéressant pour vous de penser que, par la version C # 7.3, il est possible de modifier les valeurs par référence à condition que la propriété Current de l’énumérateur renvoie un type de référence. Ce qui suit serait valide (copie textuelle des documents de MS):

 Span storage = stackalloc int[10]; int num = 0; foreach (ref int item in storage) { item = num++; } 

En savoir plus sur cette nouvelle fonctionnalité dans la déclaration de C # foreach | Microsoft Docs .