Pourquoi est La Variable d'Itération dans une instruction foreach de C# 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;
}
À venir à partir 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.
- Bonne trouvaille. Je suis un peu surpris que je n'ai jamais remarqué ça avant.
- Vous n'êtes pas en train de changer l'élément de tableau dans votre deuxième exemple soit.
- Je ne suis pas vraiment intéressé à changer l'élément de tableau. Je suis plus intéressé par pourquoi l'itérateur est immuable. Je suppose que j'ai besoin de modifier la question de la clarifier.
- Je corrige la position des mains, mon 2ème exemple manquait une ligne. Merci pour le heads up.
Vous devez vous connecter pour publier un commentaire.
Permet de commencer avec un idiot, mais un exemple illustratif:
À 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 que
o
est simplement un pointeur. Maintenant permet de faire cela dans itérateur forme.À nouveau, cela ne sert à rien. Ou au moins, il serait 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 que
o
est juste un pointeur de toute façon.Prenons votre exemple:
Étaient présent à la compilation, le
Location
dans votre exemple etoiles se référant à une valeur deMap
, mais ensuite vous changer pour faire référence à un nouveauPosition
(implicitement créé par l'opérateur d'addition). Fonctionnellement, c'est l'équivalent de ce qui NE la compilation):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 Frisé du Droit si vous suivez le Codage d'Horreur. Une variable devrait signifier qu'une seule chose.
Map[i]
, ce qui est exactement ce que nous attendez.foreach
tue tout le concept de mutable itérateurs. Je suis obligé de faire une copie lors de l'itération avec foreach et d'attribuer la copie par la suite ce qui est en effet, ineficient. Ou de les abandonner à l'aide de la syntaxe de sucre comme leforeach
en faveur de la bonne vieille for(i;i<n;i++) boucles pour ce but. En C++, notre gamme à base defor(auto& item: collection)
nous permet de faire mutable itérateurs. La seule solution est de faire réitéré l'objet classes et de l'utilisation de la mutation des méthodes sur eux. Certainement pas amusant si nous voulons muter primitives.foreach (Object o in nums) {
- n'est-ce pas que, fondamentalement, le même que le moulage d'unList<T>
enIList
? C'est parfaitement faisable. Toutefois, l'attribution arbitraireobject
viaIList
's de l'indexeur n'est pas possible: Si la valeur assignée ne correspond pas fortement typées liste type d'élément, une exception sera levée.o = "apples";
pourrait facilement être traités de la même manière.foreach
boucle ne pouvais pas travailler comme ça.Si les variables ont été mutable, qui pourraient donner une fausse impression. Par exemple:
Certaines personnes pourrait pense que ce serait de modifier le contenu du tableau. Il atteint un peu, mais il pourrait y avoir une raison. Je vais regarder dans mes annoté spec ce soir...
foreach
, conduisant à une bien étrange conception de l'OMI. Je suis content c'est la façon dont il est.foreach
" - le compilateur permetforeach
sur des choses qui mettent en œuvre desIEnumerable
. Je ne vois aucune raison pourquoi il ne peut également permettre l'affectation de la variable d'itération au sein de ...foreach
quand un autre, même condition (par exemple, unIModifyableEnumerable
interface) est remplie. En fin de compte, cependant, on s'habitue selon la manière dont est défini pour être la manière dont le langage fonctionne.ref
locales les variables en C# 7, je peux voir un cas d'utilisation pour une variable d'itération à être déclarés en tant queref
aussi bien pour les tableaux. Que serait raisonnable - mais pas seulement de faire implicitement, de l'OMI.Je pense que sa limitation artificielle, pas vraiment besoin de le faire. Pour prouver un point, prenons ce code en considiration, et d'une possible solution à votre problème. Le problème, c'est assigner, mais les changements internes d'objets ne présentent pas un problème:
Je ne savais pas à propos de ce phénomène, car j'ai toujours été de modifier les objets de la façon dont je l'ai décrit.
Lorsque vous modifiez la collecte, la modification peut avoir des effets secondaires. L'agent recenseur a aucun moyen de savoir comment traiter correctement avec ces effets secondaires, ils ont donc fait le collections immuables.
Voir aussi cette question:
Quelle est la meilleure façon de modifier une liste dans un foreach
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.
IEnumerable
/IEnumerable<T>
pour décider si ou de ne pas permettre à unforeach
boucle en premier lieu. Si, la langue, les concepteurs ont choisi d'autoriser la modification de la variable d'itération, le compilateur aurait pu être fait pour accepter de telles modifications dans les cas où laforeach
boucle est utilisée pour effectuer une itération sur unIList
/IList<T>
.La condition de travailler avec IEnumerable objets, c'est que la collection sous-jacente ne doit pas changer pendant que vous y accédez avec Énumérable. Vous pouvez le supposer que l'objet Énumérable est un aperçu de la collection d'origine. Donc si vous tente de changer la collecte lors de l'énumération, il va lever une exception. Cependant, les objets récupérés dans l'Énumération n'est pas immuable à tous.
Puisque la variable est utilisée dans la boucle foreach est locale à la boucle de bloquer cette variable est cependant pas disponible à l'extérieur du bloc.