Почему resharper предлагает обернуть переменную в массив для доступа к измененным предупреждениям о закрытии?

Учитывая следующий (сильно отредактированный псевдо) код:

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

И никаких предупреждений.

Почему использование массива безопасно?


person citizenmatt    schedule 24.06.2011    source источник
comment
В какой версии ReSharper вы это видели?   -  person John Saunders    schedule 27.11.2012


Ответы (2)


Я сам заметил то же самое в ReSharper, и мне стало интересно, почему он не предупреждает, когда значение помещено в массив. Другой ответ здесь, к сожалению, неверен и, похоже, неправильно понимает, как реализованы замыкания, поэтому подумал, что попытаюсь объяснить (что я думаю) обоснование этого рефакторинга.

Как вы видели, результат один и тот же независимо от того, обернут ли массив массивом или нет, поэтому рефакторинг на самом деле ничего не «исправляет», и те же проблемы, которые могут возникнуть при доступе к обычному модифицированному закрытию, существуют после применения изменения. Однако после изменения, поскольку сам count массив не изменяется (только его содержимое), предупреждение «Доступ к измененному закрытию» больше не актуально.

На самом деле это изменение не делает проблему более очевидной (по крайней мере, на мой взгляд), поэтому может показаться, что это предложение по существу указывает ReSharper игнорировать проблему, не прибегая к довольно грязному механизму // ReSharper disable AccessToModifiedClosure для подавления ошибка.

person Iridium    schedule 27.11.2012
comment
Кажется правдоподобным, что это их обоснование, потому что я не могу придумать никакой другой причины для этого исправления кода. Если это действительно то, почему они реализовали это, кажется странным, что они думают, что упаковка всех обращений к переменной в одноэлементном массиве будет менее беспорядочной, чем просто отключение предупреждения с комментарием только тогда, когда оно используется. - person jam40jeff; 16.12.2017

Это потому, что эти 2 типа разные. Тип int - это тип значения, а массив - тип ссылки. Это означает, что int находится в стеке, а указатель массива находится в стеке.

Когда вы обновляете тип значения, он обновляет эту часть памяти стека. Тип Reference, с другой стороны, оставляет эту часть памяти стека в покое и изменяет то, на что она указывает.

Resharper не жалуется на массив, потому что два разных метода Lambda создают замыкание вокруг памяти, указывающее, где обновить значение. Обе лямбды получают один и тот же адрес и не меняют исходный.

person Jason    schedule 24.06.2011
comment
Но, конечно же, лямбды, закрывающие переменную типа int, также указывают на одну и ту же точку в памяти, так в чем же разница? Код в обоих случаях выполняется одинаково, поэтому с точки зрения реализации разницы нет. Но я не понимаю, почему Resharper относится к ним по-другому. - person citizenmatt; 01.07.2011
comment
@citizenmatt, подумайте о случае, когда метод вернулся, ReSharper не знает, что лямбда-выражения будут работать только тогда, когда вы находитесь в вызове DoStuff (вещь) - person Ian Ringrose; 07.07.2011
comment
Это неправильная причина, посмотрите другой ответ. Нет никакой разницы в захвате ссылочных или значений переменных внутри лямбда, поэтому поведение одинаково в любом случае. stackoverflow.com/questions/5438307 / - person Matt Klein; 28.04.2015