самостоятельно изменяющееся поле только для чтения - PullRequest
0 голосов
/ 09 апреля 2011

я в курсе сообщения Эрика Липперта в блоге о ситуации , но я думаю, что это другая ситуация, потому что поле имитирует себя, а не свое поле.Как вы могли бы объяснить вызов MoveNext (), если Enumerator был доступен только для чтения, не показывает никакого эффекта и вывод всегда равен 0?

 class SomeClass
    {
        private List<int> list;
        private [readonly] List<int>.Enumerator enumerator;

        public SomeClass()
        {
            list = new List<int>() { 1, 2, 3 };
            enumerator = list.GetEnumerator();
        }

        public int ReadValue()
        {
            if (enumerator.MoveNext())
                return enumerator.Current;
            return -1;

        }
    }
static void Main()
    {
        SomeClass c = new SomeClass();
        int value;
        while ((value = c.ReadValue()) > -1)
            MessageBox.Show(value.ToString());
    }

Ответы [ 3 ]

10 голосов
/ 10 апреля 2011

Я думаю, что это другая ситуация

Вы не правы. Именно эту ситуацию я описываю в своей статье в блоге, на которую вы ссылаетесь.

Чтобы повторить мой анализ здесь: каждый вызов нестатического метода в структуре принимает параметр "ref", называемый "this". Мы не показываем параметр «ref this» в списке параметров, но он есть, сгенерированный компилятором для вас. Все, что передается с помощью ref, должно быть переменной . Поскольку переменная только для чтения может быть (и в этом случае будет изменена) посредством вызова, мы должны убедиться, что переменная только для чтения никогда не передается с помощью ref. Когда вы вызываете метод для структуры только для чтения, мы создаем временную переменную, копируем структуру во временную переменную, передаем ссылку на временную переменную как «this» в вызове метода, а затем отбрасываем временную. Это объясняет поведение, которое вы видите; каждая мутация, вызванная MoveNext, происходит с копией, которая затем отбрасывается.

Можете ли вы объяснить, почему эта ситуация - точно такая же, как та, которую я описываю в моем блоге - отличается? Что, по вашему мнению, отличается в счетчиках, что делает их особенными?

2 голосов
/ 09 апреля 2011

Ключевое слово readonly затрудняет работу компилятора C #. Он не может знать, есть ли у MoveNext () и Current побочные эффекты, которые нарушают контракт только для чтения. MoveNext (), безусловно, делает. Таким образом, чтобы сгенерировать правильный код, он должен создать копию значения итератора. Это происходит дважды, один раз для вызова метода MoveNext (), снова при чтении свойства Current. Это легко увидеть, запустив в своей программе ildasm.exe, копия которого называется CS $ 0 $ 0001 в сборке отладки.

Было бы неплохо, если бы компилятор хотя бы сгенерировал предупреждение для этого кода. Очень трудно сделать точно, хотя, действительно нужно знать, есть ли у участника побочный эффект. Это не знает. way слишком много структурных типов со средствами получения свойств, которые не имеют побочного эффекта, поэтому просто всегда генерировать предупреждение невозможно.

Тип функции, которая потребуется, чтобы сообщить об этом, - это ключевое слово const , как оно используется в C ++. Метод может быть объявлен как const, чтобы указать, что он не изменяет состояние объекта. Я серьезно сомневаюсь, что эта функция когда-нибудь превратится в язык C #, хотя написание const-правильного кода не так просто и, честно говоря, немного лаконично.

2 голосов
/ 09 апреля 2011

Если я понимаю сообщение Эрика Липперта в блоге по этому вопросу, которое вы разместили, заявление

if(enumerator.MoveNext())

сначала создает копию перечислителя, который MoveNext () выполняется для копии. Эта копия умирает и следующая строка:

return enumerator.Current;

возвращает Текущий исходный перечислитель, а не копию, поэтому вы всегда получаете 0

...