Учитывая следующий (сильно отредактированный псевдо) код:
int count = 0;
thing.Stub(m => m.AddBlah()).WhenCalled(o => count++);
thing.Stub(m => m.RemoveBlah()).WhenCalled(o => count--);
DoStuff(thing);
Assert.AreEqual(1, count);
ReSharper выдает предупреждение о счетчике - "Доступ к измененному закрытию". Я понимаю, почему я получаю это предупреждение (переменная count изменяется в двух разных лямбдах и, вероятно, имеет нежелательную семантику), но я не понимаю совета ReSharper: "Оберните локальную переменную в массив". Если я позволю ReSharper сделать это, я получу:
int count[] = { 0 };
thing.Stub(m => m.AddBlah()).WhenCalled(o => count[0]++);
thing.Stub(m => m.RemoveBlah()).WhenCalled(o => count[0]--);
DoStuff(thing);
Assert.AreEqual(1, count[0]);
И никаких предупреждений.
Почему использование массива безопасно?
Ответ 1 · рейтинг 13 · Iridium
Я сам заметил то же самое в ReSharper, и мне стало интересно, почему он не предупреждает, когда значение помещено в массив. Другой ответ здесь, к сожалению, неверен и, похоже, неправильно понимает, как реализованы замыкания, поэтому подумал, что попытаюсь объяснить (что я думаю) обоснование этого рефакторинга.
Как вы видели, результат один и тот же независимо от того, обернут ли массив массивом или нет, поэтому рефакторинг на самом деле ничего не "исправляет", и те же проблемы, которые могут возникнуть при доступе к обычному модифицированному закрытию, существуют после применения изменения. Однако после изменения, поскольку сам count массив не изменяется (только его содержимое), предупреждение "Доступ к измененному закрытию" больше не актуально.
На самом деле это изменение не делает проблему более очевидной (по крайней мере, на мой взгляд), поэтому может показаться, что это предложение по существу указывает ReSharper игнорировать проблему, не прибегая к довольно грязному механизму // ReSharper disable AccessToModifiedClosure для подавления ошибка.
Ответ 2 · рейтинг 1 · Jason
Это потому, что эти 2 типа разные. Тип int - это тип значения, а массив - тип ссылки. Это означает, что int находится в стеке, а указатель массива находится в стеке.
Когда вы обновляете тип значения, он обновляет эту часть памяти стека. Тип Reference, с другой стороны, оставляет эту часть памяти стека в покое и изменяет то, на что она указывает.
Resharper не жалуется на массив, потому что два разных метода Lambda создают замыкание вокруг памяти, указывающее, где обновить значение. Обе лямбды получают один и тот же адрес и не меняют исходный.