42 votes

Pourquoi la variable d'itération dans une instruction C # foreach est-elle en lecture seule?

Ce que je comprends, C#foreach variable d'itération est immuable.

Ce qui signifie que je ne peux pas modifier l'itérateur comme ceci:

foreach (Position Location in Map)
{
     //We want to fudge the position to hide the exact coordinates
     Location = Location + Random();     //Compiler Error

     Plot(Location);
}

Je ne peux pas modifier la variable d'itérateur directement et au lieu de cela, je dois utiliser une boucle for

for (int i = 0; i < Map.Count; i++)
{
     Position Location = Map[i];
     Location = Location + Random();

     Plot(Location);        
     i = Location;
}

Venant d'un C++ fond, je vois foreach comme une alternative à la boucle for. Mais avec la restriction ci-dessus, j'ai l'habitude de secours à l'aide de la boucle for.

Je suis curieux, quel est l'intérêt de faire de l'itérateur immuable?


Edit:

Cette question est plus de la curiosité et non comme une question de codage. J'ai apprécié le codage des réponses, mais je ne peux pas les marquer comme réponses.

Aussi, l'exemple ci-dessus a été simplifiée. Voici un exemple C++ de ce que je veux faire:

// The game's rules: 
//   - The "Laser Of Death (tm)" moves around the game board from the
//     start area (index 0) until the end area (index BoardSize)
//   - If the Laser hits a teleporter, destroy that teleporter on the
//     board and move the Laser to the square where the teleporter 
//     points to
//   - If the Laser hits a player, deal 15 damage and stop the laser.

for (int i = 0; i < BoardSize; i++)
{
    if (GetItem(Board[i]) == Teleporter)
    {
        TeleportSquare = GetTeleportSquare(Board[i]);
        SetItem(Board[i], FreeSpace);
        i = TeleportSquare;
    }

    if (GetItem(Board[i]) == Player)
    {
        Player.Life -= 15;
        break;
    }
}

Je ne peux pas faire ci-dessus en C#foreach parce que l'itérateur i est immuable. Je pense (corrigez-moi si je me trompe), c'est spécifique à la conception de foreach dans les langues.

Je suis intéressé par pourquoi le foreach itérateur est immuable.

26voto

tylerl Points 14541

Permet de commencer avec un idiot, mais un exemple illustratif:

Object o = 15;
o = "apples";

À aucun moment, ne nous avons l'impression que l'on vient d'avoir le numéro 15 dans une chaîne de pommes. Nous savons qu' o est simplement un pointeur. Maintenant permet de faire cela dans itérateur forme.

int[] nums = { 15, 16, 17 };

foreach (Object o in nums) {
     o = "apples";
}

De nouveau, cela ne sert à rien. Ou au moins, il allait accomplir de rien n'était pour compiler. Il n'aurions certainement pas insérer notre chaîne dans le tableau int -- ce n'est pas permis, et nous savons qu' o est un pointeur de toute façon.

Prenons votre exemple:

foreach (Position Location in Map)
{
     //We want to fudge the position to hide the exact coordinates
     Location = Location + Random();     //Compiler Error

     Plot(Location);
}

Étaient présent à la compilation, l' Location dans votre exemple etoiles se référant à une valeur en Map, mais ensuite vous changer pour faire référence à un nouveau Position (implicitement créé par l'opérateur d'addition). Fonctionnellement, c'est l'équivalent de ce qui NE la compilation):

foreach (Position Location in Map)
{
     //We want to fudge the position to hide the exact coordinates
     Position Location2 = Location + Random();     //No more Error

     Plot(Location2);
}

Alors, pourquoi est-ce que Microsoft vous interdire de re-affectation de le pointeur utilisé pour l'itération? De la clarté, pour une chose, vous ne voulez pas que les gens de l'affectation à ce qu'elle pense qu'ils ont changé de position à l'intérieur de la boucle. Facilité de mise en œuvre pour l'autre: La variable peut cacher des logiques internes indiquant l'état de la boucle en cours.

Mais plus important encore, il n'y a aucune raison pour que vous voulez lui attribuer. Il représente l'élément courant de la boucle de la séquence. Affectation d'une valeur à elle rompt le "Principe de Responsabilité Unique" ou Bouclés de la Loi si vous suivez le Codage d'Horreur. Une variable devrait signifier qu'une seule chose.

14voto

Jon Skeet Points 692016

Si la variable était modifiable, cela pourrait donner une impression incorrecte. Par exemple:

 string[] names = { "Jon", "Holly", "Tom", "Robin", "William" };

foreach (string name in names)
{
    name = name + " Skeet";
}
 

Certaines personnes pourraient penser que cela changerait le contenu du tableau. Cela atteint un peu, mais cela pourrait être une raison. Je vais le chercher dans mes spécifications annotées ce soir ...

11voto

majkinetor Points 2375

Je pense que sa limitation artificielle, pas vraiment besoin de le faire. Pour prouver le point, prenez ce code en considération et une solution possible à votre problème. Le problème est d'affecter, mais les changements internes d'objets ne posent pas de problème:

 using System;
using System.Collections.Generic;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {

            List<MyObject> colection =new List<MyObject>{ new MyObject{ id=1, Name="obj1" }, 
                                                    new MyObject{ id=2, Name="obj2"} };

            foreach (MyObject b in colection)
            {
             // b += 3;     //Doesn't work
                b.Add(3);   //Works
            }
        }

        class MyObject
        {
            public static MyObject operator +(MyObject b1, int b2)
            {
                return new MyObject { id = b1.id + b2, Name = b1.Name };
            }

          public void Add(int b2)
          {
              this.id += b2;
          }
            public string Name;
            public int id;
        }
    }
}
 

Je ne connaissais pas ce phénomène, car je modifiais toujours les objets de la manière que je décrivais.

4voto

Rik Points 12802

Lorsque vous modifiez la collection, la modification peut avoir des effets secondaires imprévisibles. L'énumérateur n'a aucun moyen de savoir comment gérer correctement ces effets secondaires, ils ont donc rendu les collections immuables.

Voir aussi cette question: Quelle est la meilleure façon de modifier une liste dans un foreach

1voto

Ian Points 13892

L'instruction foreach fonctionne avec Énumérable. La chose qui est différent, avec une Énumération, c'est qu'il ne sait pas à propos de l'ensemble de cette collection, il est presque aveugle.

Cela signifie qu'il ne sait pas ce que seront les prochaines, ou si en fait il n'y a rien jusqu'à la prochaine itération du cycle. C'est pourquoi vous voyez là .ToList() beaucoup de sorte que les gens peuvent prendre un nombre de Énumérable.

Pour une raison que je ne suis pas sûr à ce sujet, cela signifie que vous devez vous assurer que votre collection n'est pas le changement que vous essayez de les déplacer le long de l'agent recenseur.

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