为什么C#foreach语句中的迭代变量是只读的?

MrV*_*dez 35 c# language-design

据我了解,C#的foreach迭代变量是不可变的.

这意味着我不能像这样修改迭代器:

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

     Plot(Location);
}
Run Code Online (Sandbox Code Playgroud)

我无法直接修改迭代器变量,而是必须使用for循环

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

     Plot(Location);        
     i = Location;
}
Run Code Online (Sandbox Code Playgroud)

来自C++背景,我认为foreach是for循环的替代品.但是由于上述限制,我通常会回退使用for循环.

我很好奇,使迭代器不可变的原理是什么?


编辑:

这个问题更多的是一个好奇的问题,而不是一个编码问题.我很欣赏编码答案,但我不能将它们标记为答案.

此外,上面的例子过于简化了.这是我想要做的C++示例:

// 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;
    }
}
Run Code Online (Sandbox Code Playgroud)

我无法在C#的foreach中执行上述操作,因为迭代器是不可变的.我想(如果我错了,请纠正我),这是针对foreach语言的设计.

我对为什么foreach迭代器是不可变的感兴趣.

tyl*_*erl 25

让我们从一个愚蠢但说明性的例子开始:

Object o = 15;
o = "apples";
Run Code Online (Sandbox Code Playgroud)

在任何时候我们都不会觉得我们只是把数字15变成了一串苹果.我们知道这o只是一个指针.现在让我们以迭代器的形式执行此操作.

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

foreach (Object o in nums) {
     o = "apples";
}
Run Code Online (Sandbox Code Playgroud)

再次,这真的没有任何成就.或者至少它不会完成任何编译.它肯定不会将我们的字符串插入到int数组中 - 这是不允许的,我们知道这o只是一个指针.

我们举个例子:

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

     Plot(Location);
}
Run Code Online (Sandbox Code Playgroud)

如果要进行编译,则Location在您的示例中指出一个值Map,但是然后您将其更改为引用一个新的Position(由加法运算符隐式创建).在功能上它等同于此(编译):

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);
}
Run Code Online (Sandbox Code Playgroud)

那么,为什么Microsoft禁止您重新分配用于迭代的指针?清楚一件事 - 你不希望人们分配它认为他们已经改变了你在循环中的位置.易于实现另一个:变量可能隐藏一些内部逻辑,指示正在进行的循环的状态.

但更重要的是,您没有理由想要分配它.它表示循环序列的当前元素.如果您遵循编码恐怖,为其分配值会违反"单一责任原则"或" 卷曲定律 ".变量应该仅仅意味着一件事.

  • Tyler,我可能错了,但我不同意你的断言"Postion Location"或"Object O"是迭代器.相反,它们是迭代变量,它们具有对它们进行操作的迭代器.如果情况确实如此,那么我认为你的推理可能有些偏差.我同意改变迭代变量的值可能会导致清晰度问题,但我确实希望微软将其留给开发人员来决定.我发现了一些更改foreach迭代变量值的实例. (3认同)

Jon*_*eet 14

如果变量是可变的,那可能会给出错误的印象.例如:

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

foreach (string name in names)
{
    name = name + " Skeet";
}
Run Code Online (Sandbox Code Playgroud)

有些人可能认为会改变数组内容.它达到了一点,但这可能是一个原因.我今晚将在我的注释规范中查找...

  • 你有没有在你的规范中看到这个?我假设设计师为了清晰起见,只是为了你的建议,但是知道是否还有其他注意事项会很有趣! (3认同)

maj*_*tor 10

我认为它是人为的限制,不需要这么做.为证明这一点,请将此代码纳入考虑范围,并为您的问题提供可能的解决方案.问题是asign,但对象的内部更改不会出现问题:

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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我不知道这个现象,因为我总是按照我描述的方式修改对象.