Компилятор C # должен давать предупреждение, но не делает? - PullRequest
18 голосов
/ 30 апреля 2010

Кто-то из моей команды попытался исправить предупреждение «переменная не используется» в пустом предложении catch.

try { ... } catch (Exception ex) { }

-> выдает предупреждение о том, что ex не используется. Пока все хорошо.

Исправление было примерно таким:

try { ... } catch (Exception ex) { string s = ex.Message; }

Увидев это, я подумал: «Просто замечательно, поэтому теперь компилятор будет жаловаться на то, что s не используется».

Но это не так! Там нет никаких предупреждений на этот кусок кода, и я не могу понять, почему. Есть идеи?

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

try { ... } catch (Exception) { }

или

try { ... } catch { }

Ответы [ 7 ]

25 голосов
/ 30 апреля 2010

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

Причина в том, что C # - это язык для сборки мусора, хотите верьте, хотите нет.

Как вы это понимаете?

Хорошо, рассмотрим следующее.

У вас есть программа, которая вызывает метод DoIt (), который возвращает строку. У вас нет исходного кода для DoIt (), но вы хотите проверить в отладчике его возвращаемое значение.

Теперь в вашем конкретном случае вы используете DoIt () для его побочных эффектов, а не для его возвращаемого значения. Так вы говорите

DoIt(); // discard the return value

Теперь вы отлаживаете свою программу и смотрите на возвращаемое значение DoIt () и его там нет, потому что к тому времени, когда отладчик прерывает работу после вызова DoIt (), сборщик мусора может иметь уже вычистил неиспользованную строку .

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

Теперь можно утверждать, что это полезная функция и что команда отладчика должна добавить функцию, посредством которой возвращаемые значения сохраняются, если сразу после выполнения метода существует точка останова отладчика. Это было бы хорошей особенностью, но я не тот человек, чтобы просить об этом; иди спроси команду отладчика.

Что делать бедному разработчику на C #? Создайте локальную переменную, сохраните результат в локальной переменной, а затем изучите локальную переменную в отладчике. Отладчик гарантирует, что местные жители не будут собирать мусор агрессивно.

Итак, вы делаете это, и затем компилятор выдает предупреждение, что у вас есть локальный файл, который только записывается и никогда не читается , потому что выполнение чтения не является частью программы, это сидит разработчик там смотрю отладчик . Это очень раздражающий пользовательский опыт! Поэтому мы обнаруживаем ситуацию, когда непостоянное значение присваивается локальной переменной или полю , которое никогда не будет читать , и подавляем это предупреждение. Если вы измените свой код так, чтобы вместо него было написано string s = "hello";, тогда вы начнете получать предупреждение, потому что компилятор рассуждает, ну, это не может быть тот, кто работает над ограничениями отладчика, потому что значение прямо там , где он может быть прочитан разработчиком уже без отладчика.

Это объясняет это. Есть множество других случаев, когда мы подавляем предупреждения о переменных, которые никогда не читаются; подробное изложение всех политик компилятора для того, когда мы сообщаем о предупреждениях, а когда мы не делаем, у меня заняло бы некоторое время, чтобы написать, поэтому я думаю, что я оставлю это на этом.

9 голосов
/ 30 апреля 2010

Переменная s используется ... для хранения ссылки на ex.Message. Если бы у вас была только строка s; Вы получите предупреждение.

1 голос
/ 30 апреля 2010

Решарпер поймает это

1 голос
/ 30 апреля 2010

В действительности компилятором не является работа над каждым конкретным случаем и случаем, когда переменная может или не может использоваться. Некоторые из них легко обнаружить, некоторые более проблематичны. Ошибочная сторона предостережения - разумная вещь (особенно, когда предупреждения могут быть расценены как ошибки - представьте, если программное обеспечение не компилировалось только потому, что компилятор думал, что вы не используете то, что использовали). Команда Microsoft Compiler специально говорит:

"... наше руководство для клиентов, которые заинтересованы в обнаружении неиспользованных элементы в их коде использовать FxCop. Он может обнаружить неиспользуемые поля и еще много интересных данных о твой код. "

- Эд Маурер, ведущий разработчик, платформа управляемого компилятора

1 голос
/ 30 апреля 2010

Свойства - это просто методы, и ничто не мешает кому-то поместить код, который что-то делает, в свойство ex.Message. Поэтому, хотя вы, возможно, ничего не делаете с s, вызов ex.Message МОЖЕТ потенциально иметь значение ....

1 голос
/ 30 апреля 2010

Я думаю, что человеку, который ответит на это, потребуется некоторое понимание того, как работает компилятор. Тем не менее, что-то вроде FxCop, вероятно, поймает это.

0 голосов
/ 30 апреля 2010

Статический анализ несколько ограничен в том, что он может сделать сегодня. (Хотя, как указал Эрик, не потому, что в этом случае он не знает.)

Новые кодовые контракты в .NET 4 значительно улучшают статическую проверку, и однажды я уверен, что вы получите больше помощи с такими очевидными ошибками, как эта.

Если вы попробовали Code Contracts, вы, тем не менее, будете знать, что проводить исчерпывающий статический анализ вашего кода нелегко - он может зависать в течение минут после каждой компиляции. Сможет ли статический анализ когда-нибудь найти такую ​​проблему во время компиляции? Вероятно, нет: см. http://en.wikipedia.org/wiki/Halting_problem.

...