Почему Повторение является Переменным в операторе C# foreach, только для чтения?

Сначала я вижу проблему, состоящую в том, что ваш конструктор по умолчанию и конструктор копирования не инициализируют myGrid. то, что вы делаете в них, создаст дополнительный массив с тем же именем, которое «затеняет» myGrid. вместо этого вы должны сделать:

Grid::Grid(const Grid& g2)
{
    size = g2.size;
    row = g2.row;
    column = g2.column;
    num_living = g2.num_living;

    myGrid = new char*[row]; // removed "char**" at the start of this line
    for(int i = 0; i < row; i++)
        myGrid[i] = new char[column];

    for(int i1 = 0; i1 < row; i1++)
    {
        for(int i2 = 0; i2 < column; i2++)
        {
            //copy(&g2[i1][i2], &g2[i1][i2]+row*column,&myGrid[i1][i2]);
            myGrid[i1][i2] = g2.get(i1,i2);
        }
    }
}

ваш конструктор по умолчанию имеет ту же проблему. но обратите внимание, что вы не можете инициализировать его с помощью фигурных скобок. но вам не нужно иметь конструктор по умолчанию, если вы его не используете.

35
задан John Saunders 23 April 2009 в 03:23
поделиться

6 ответов

Давайте начнем с глупого, но наглядного примера:

Object o = 15;
o = "apples";

Ни в коем случае не создается впечатление, что мы просто превратили число 15 в ряд яблок. Мы знаем, что o - просто указатель. Теперь давайте сделаем это в форме итератора.

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

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

Опять же, это действительно ничего не дает. Или, по крайней мере, он не смог бы ничего не сделать, если бы его компилировали. Конечно, она не вставит нашу строку в массив 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);
}

Была ли это компилирована , Местоположение в вашем примере перестает ссылаться на значение в Map , но затем вы меняете его, ссылаясь на новую позицию (неявно созданную добавлением оператор). Функционально это Это эквивалентно этому (что компилируется):

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);
}

Итак, почему Microsoft запрещает вам переназначать указатель, используемый для итерации? Ясность с одной стороны - вы не хотите, чтобы люди, назначенные на это, думали, что они изменили вашу позицию в цикле. Простота реализации для другого: переменная может скрывать некоторую внутреннюю логику, указывающую состояние текущего цикла.

Но, что еще важнее, у вас нет причин, чтобы хотеть назначить его. Он представляет текущий элемент последовательности цикла. Присвоение ему значения нарушает «Принцип единой ответственности» или Закон Керли , если вы следуете Кодовому Ужасу. Переменная должна означать только одно.

почему Microsoft запрещает вам переназначать указатель, используемый для итерации? Ясность с одной стороны - вы не хотите, чтобы люди, назначенные на это, думали, что они изменили вашу позицию в цикле. Простота реализации для другого: переменная может скрывать некоторую внутреннюю логику, указывающую состояние текущего цикла.

Но, что еще важнее, у вас нет причин, чтобы хотеть назначить его. Он представляет текущий элемент последовательности цикла. Присвоение ему значения нарушает «Принцип единой ответственности» или Закон Керли , если вы следуете Кодовому Ужасу. Переменная должна означать только одно.

почему Microsoft запрещает вам переназначать указатель, используемый для итерации? Ясность с одной стороны - вы не хотите, чтобы люди, назначенные на это, думали, что они изменили вашу позицию в цикле. Простота реализации для другого: переменная может скрывать некоторую внутреннюю логику, указывающую состояние текущего цикла.

Но, что еще важнее, у вас нет причин, чтобы хотеть назначить его. Он представляет текущий элемент последовательности цикла. Присвоение ему значения нарушает «Принцип единой ответственности» или Закон Керли , если вы следуете Кодовому Ужасу. Переменная должна означать только одно.

Переменная может скрывать некоторую внутреннюю логику, указывающую на состояние цикла в процессе.

Но, что еще важнее, у вас нет причин, чтобы хотеть назначить его. Он представляет текущий элемент последовательности цикла. Присвоение ему значения нарушает «Принцип единой ответственности» или Закон Керли , если вы следуете Кодовому Ужасу. Переменная должна означать только одно.

Переменная может скрывать некоторую внутреннюю логику, указывающую на состояние цикла в процессе.

Но, что еще важнее, у вас нет причин, чтобы хотеть назначить его. Он представляет текущий элемент последовательности цикла. Присвоение ему значения нарушает «Принцип единой ответственности» или Закон Керли , если вы следуете Кодовому Ужасу. Переменная должна означать только одно.

25
ответ дан 27 November 2019 в 07:13
поделиться

Если бы переменная была изменяемой, это могло бы создать неправильное впечатление. Например:

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

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

Некоторые люди могут подумать, что это изменит содержимое массива. Достигает немного, но это может быть причиной. Я посмотрю это сегодня в своей аннотированной спецификации ...

14
ответ дан 27 November 2019 в 07:13
поделиться

Я думаю, что это искусственное ограничение, в действительности нет необходимости делать это , Чтобы доказать это, примите этот код к сведению и возможное решение вашей проблемы. Проблема заключается в выравнивании, но внутренние изменения объектов не представляют проблемы:

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;
        }
    }
}

Я не знал об этом явлении, потому что я всегда модифицировал объекты так, как я описал.

10
ответ дан 27 November 2019 в 07:13
поделиться

Оператор foreach работает с Enumerable. Что отличается от Enumerable, так это то, что он не знает о коллекции в целом, он почти слепой.

Это означает, что он не знает, что будет дальше, или, действительно, есть что-то до следующего итерационный цикл. Вот почему там вы видите .ToList () много, чтобы люди могли получить счетчик Enumerable.

По какой-то причине, в которой я не уверен, это означает, что вам нужно убедиться, что ваша коллекция не меняется, так как вы попробуйте переместить счетчик.

1
ответ дан 27 November 2019 в 07:13
поделиться

При изменении коллекции изменение может иметь непредсказуемые побочные эффекты. Перечислитель не может знать, как правильно справляться с этими побочными эффектами, поэтому они сделали коллекции неизменяемыми.

Смотрите также этот вопрос: Как лучше всего изменить список в foreach

3
ответ дан 27 November 2019 в 07:13
поделиться

Условием работы с объектами IEnumerable является то, что коллекция не должна изменяться, пока вы обращаетесь к ней с помощью Enumerable. Можно предположить, что объект Enumerable является моментальным снимком исходной коллекции. Поэтому, если вы попытаетесь изменить коллекцию при перечислении, она выдаст исключение. Однако извлеченные объекты в Enumeration не являются неизменяемыми вообще.

1
ответ дан 27 November 2019 в 07:13
поделиться
Другие вопросы по тегам:

Похожие вопросы: