En C #, pourquoi ne puis-je pas modifier le membre d’une instance de type valeur dans une boucle foreach?

Je sais que les types de valeur doivent être immuables, mais ce n’est qu’une suggestion, pas une règle, n’est-ce pas? Alors pourquoi je ne peux pas faire quelque chose comme ça:

struct MyStruct { public ssortingng Name { get; set; } } public class Program { static void Main(ssortingng[] args) { MyStruct[] array = new MyStruct[] { new MyStruct { Name = "1" }, new MyStruct { Name = "2" } }; foreach (var item in array) { item.Name = "3"; } //for (int i = 0; i < array.Length; i++) //{ // array[i].Name = "3"; //} Console.ReadLine(); } } 

La boucle foreach dans le code ne comstack pas alors que la boucle commentée fonctionne correctement. Le message d’erreur:

Impossible de modifier les membres de ‘item’ car il s’agit d’une variable d’itération ‘foreach’

Pourquoi donc?

Car foreach utilise un énumérateur et les énumérateurs ne peuvent pas modifier la collection sous-jacente, mais peuvent toutefois modifier les objects référencés par un object de la collection. C’est là que la sémantique de type valeur et référence entre en jeu.

Sur un type de référence, c’est-à-dire une classe, toute la collection stockée est une référence à un object. En tant que tel, il ne touche jamais aucun membre de l’object, et s’en moque. Un changement à l’object ne touchera pas la collection.

Par contre, les types de valeur stockent toute leur structure dans la collection. Vous ne pouvez pas toucher ses membres sans changer la collection et invalider l’énumérateur.

De plus, l’énumérateur renvoie une copie de la valeur dans la collection. Dans un type ref, cela ne veut rien dire. Une copie d’une référence sera la même référence et vous pouvez modifier l’object référencé comme vous le souhaitez avec les modifications hors de scope. Sur un type valeur, en revanche, tout ce que vous obtenez est une copie de l’object, et par conséquent, toute modification sur cette copie ne se propagera jamais.

C’est une suggestion en ce sens qu’il n’y a rien qui vous empêche de le violer, mais il faut vraiment lui accorder beaucoup plus de poids que “c’est une suggestion”. Par exemple, pour les raisons que vous voyez ici.

Les types de valeur stockent la valeur réelle dans une variable, pas une référence. Cela signifie que vous avez la valeur dans votre tableau, et vous avez une copie de cette valeur dans l’ item , pas une référence. Si vous étiez autorisé à modifier les valeurs dans item , cela ne serait pas reflété sur la valeur dans le tableau car il s’agit d’une valeur complètement nouvelle. C’est pourquoi ce n’est pas autorisé.

Si vous voulez faire cela, vous devrez parcourir le tableau par index et ne pas utiliser de variable temporaire.

Les structures sont des types de valeur.
Les classes sont des types de référence.

ForEach construction ForEach utilise IEnumerator des éléments de type de données IEnumerable . Lorsque cela se produit, alors les variables sont en lecture seule dans le sens où vous ne pouvez pas les modifier et, comme vous avez un type de valeur, vous ne pouvez modifier aucune valeur contenue car elle partage la même mémoire.

C # lang spec section 8.8.4:

La variable d’itération correspond à une variable locale en lecture seule dont l’étendue s’étend sur l’instruction incorporée

Pour corriger cette utilisation de classe insted de structure;

 class MyStruct { public ssortingng Name { get; set; } } 

Edit: @CuiPengFei

Si vous utilisez le type implicite var , il est plus difficile pour le compilateur de vous aider. Si vous MyStruct cela vous dirait en cas de structure que c’est en lecture seule. Dans le cas de classes, la référence à l’élément est en lecture seule, vous ne pouvez donc pas écrire item = null; à l’intérieur de la boucle, mais vous pouvez changer ses propriétés, qui sont modifiables.

Vous pouvez également utiliser (si vous souhaitez utiliser struct ):

  MyStruct[] array = new MyStruct[] { new MyStruct { Name = "1" }, new MyStruct { Name = "2" } }; for (int index=0; index < array.Length; index++) { var item = array[index]; item.Name = "3"; } 

REMARQUE: selon le commentaire d’Adam, ce n’est pas la bonne réponse / cause du problème. C’est quand même quelque chose qui mérite d’être considéré.

De MSDN . Vous ne pouvez pas modifier les valeurs lorsque vous utilisez l’énumérateur, ce qui est essentiellement ce que fait foreach .

Les énumérateurs peuvent être utilisés pour lire les données dans la collection, mais ils ne peuvent pas être utilisés pour modifier la collection sous-jacente.

Un énumérateur rest valide tant que la collection rest inchangée. Si des modifications sont apscopes à la collection, telles que l’ajout, la modification ou la suppression d’éléments, l’énumérateur est irrémédiablement invalidé et son comportement n’est pas défini.

Faites de MyStruct une classe (au lieu de struct) et vous pourrez le faire.

Je pense que vous pouvez trouver la réponse ci-dessous.

Foreach struct erreur de compilation bizarre en C #

Les types de valeur sont appelés par valeur . En bref, lorsque vous évaluez la variable, une copie en est faite. Même si cela était autorisé, vous MyStruct une copie plutôt que l’instance d’origine de MyStruct .

foreach est en lecture seule en C # . Pour les types de référence (classe), cela ne change pas grand chose, car seule la référence est en lecture seule, vous avez donc toujours le droit de faire ce qui suit:

  MyClass[] array = new MyClass[] { new MyClass { Name = "1" }, new MyClass { Name = "2" } }; foreach ( var item in array ) { item.Name = "3"; } 

Pour les types de valeur cependant (struct), l’object entier est en lecture seule, ce qui entraîne ce que vous rencontrez. Vous ne pouvez pas ajuster l’object dans le foreach.