Профилактическое и реактивное программирование на С#
Мне всегда приходилось ошибаться в предотвращении исключений, никогда не предпринимая никаких действий, если я не уверен, что ошибок не будет. Я научился программировать на C, и это был единственный способ действительно сделать что-то.
Работа с С# Я часто вижу более реактивную программирование - пытаюсь что-то сделать и обрабатывать исключения. Для меня это похоже на использование исключений в качестве контрольных операторов. Первые несколько раз, когда я видел это, я отклонил это как плохую практику. Но за последние несколько месяцев я видел это повсюду и просто должен удивляться - это приемлемая/эффективная или просто эпидемия?
Обновление:
Для немного разъяснения большая часть обработки исключений, которую я вижу, это такие вещи, как
try
{
//open file
}
catch
{
//message box for file not found
}
или даже хуже
try
{
//open xml
//modify xml (100+ lines of code)
}
catch
{
//message for 'unspecified error'
}
Я понимаю, что времена, когда обработка исключений очень хороша в использовании (например, подключения к базе данных), но я имею в виду использование исключений вместо более "традиционного" управления. Я спросил об этом, потому что я чувствовал, что этот стиль программирования использует исключения как костыль, а не как метод восстановления, и хотел знать, было ли это что-то, чему я должен был научиться ожидать в мире С#.
Ответы
Ответ 1
Вы можете, конечно, злоупотреблять исключениями, это С#. На мой взгляд, вы никогда не должны получать ArgumentNullException, так как вы всегда должны сначала проверять значение null. Тем не менее, есть также много случаев, когда вы не можете проверять свой путь вне исключения. Все, что взаимодействует с "внешним миром" (подключение к веб-серверу, базе данных и т.д.), Может вызвать исключение.
Предотвратите как можно больше, но вам по-прежнему нужна способность реагировать на все остальное.
Ответ 2
Как обычно, ответ "зависит", но я вообще соглашаюсь с философией "fail fast".
Я предпочитаю использовать try/finally (sans catch), если только я не могу сделать что-то полезное для восстановления из исключения в конкретном блоке кода. Ловить все возможные исключения не стоит. В общем случае отказ в сбое предпочтительнее, если вы будете молча работать.
Если, с другой стороны, вы знаете, как восстановить из определенного исключения, тогда да, сделайте это.
Скажем, у вас есть библиотека передачи файлов. Вероятно, это приведет к исключению, если передача прерывается из-за таймаута или сбоя сети. Это разумно. Вы будете раздражены, если библиотека просто терпит неудачу; проверка кода возврата гораздо более подвержена ошибкам и не обязательно более читаема. Но, возможно, у вас есть бизнес-правило для отправки кучи файлов на сервер, в котором вы должны сделать как минимум 3 попытки передать файл, прежде чем отказаться от него и попросить вмешательство пользователя. В этом случае бизнес-логика должна обрабатывать исключение, пытаться восстановить, а затем делать все, что он должен делать, когда автоматическое решение выходит из строя (предупреждать пользователя, планировать последующую попытку или что-то еще).
Если вы найдете код, который делает это:
try
{
// do something that could throw
// ...
}
catch {} //swallow the exception
или
catch { return null; }
Этот вероятно сломан. Конечно, иногда код, который вы вызываете, может вызвать исключение, которое вам действительно неинтересно. Но я часто вижу, что люди делают это просто так, что им не нужно "обрабатывать" исключение вверх по течению; практика усложняет процесс отладки.
Некоторые считают, что исключения исключают каскадную цепочку ответственности, чтобы быть плохим, потому что вы просто "надеетесь", что кто-то наверху "чудесным образом" знает, что делать. Эти люди ошибаются. Код восходящего потока часто является единственным местом, в котором может знать, что делать.
Иногда я попробую/поймаю и выброшу другое, более подходящее исключение. Однако, когда это возможно, положение охраны лучше. например. if (argument==null) throw new ArgumentNullException();
лучше, чем позволять исключению NullReferenceException распространять стек вызовов, потому что он ясно, что пошло не так.
Некоторые условия "никогда не должны происходить" или "я не знал, что это может произойти", вероятно, должно быть зарегистрировано (см., например, ведение журнала jboss), но может быть проглочено до того, как они снимут ваше приложение, по крайней мере, в некоторых случаев.
ETA. Вероятно, он нарушен, чтобы принять конкретное исключение, а затем отобразить общее неоднозначное сообщение об ошибке. Для вашего второго примера выше это звучит плохо для меня. Для вашего первого, "Файл не найден", это может быть более разумным (если вы действительно поймаете это конкретное исключение, а не просто "все" ), если у вас нет лучшего способа справиться с этим условием в другом месте. Модальные почтовые ящики, как правило, плохо "запах дизайна взаимодействия" для меня, но это в основном не относится к делу.
Ответ 3
Есть случаи, когда вы вынуждены реагировать: проверка и работа не всегда работают.
Доступ к файлу на диске: работает ли диск? существует ли файл? могу ли я получить доступ для чтения к файлу?
Доступ к базе данных: сервер принимает соединения? Могут ли мои верительные грамоты? Существуют ли объекты базы данных? Соответствуют ли столбцы соответствующим именам?
Все эти вещи могут меняться между проверкой и операцией.
Ответ 4
В мои дни С++ COM я научился не перегибаться назад, чтобы обрабатывать "возможные" ошибки, то есть я не проверял регулярно возвращаемое значение каждого вызова функции. Это потому, что я знаю, что мне не удастся справиться с неизвестными условиями:
-
Я не знаю, когда это произойдет, или что это значит.
-
Я не могу это сделать, поэтому я не могу проверить свой код обработки ошибок. Код, который не протестирован, не является кодом, который не работает.
-
Я не знаю, что пользователь хотел бы мне сделать.
-
Рутинная обработка ошибок может позволить программе продолжить работу, но не надежно и предсказуемо, что пользователь выиграет.
-
Неудача быстро и резко (вместо медленного и тонкого) не убаюкивает пользователя в ложном смысле безопасности и дает программисту больше шансов диагностировать проблему.
Очевидно, я не говорю "никогда не обрабатывать ошибки", я говорю "обрабатывать ошибки только тогда, когда вы можете добавить значение".
Те же принципы применяются к С#. Я думаю, что отличная идея поймать исключение, если вы можете обернуть его в более подходящее исключение и бросить это вместо этого. И если вы точно знаете, как ваш пользователь выиграет от вас, обратившись за исключением, тогда идите. Но в противном случае оставьте это в покое.
Ответ 5
Я считаю, что вы правы в том, что использование исключений в качестве контрольных утверждений является плохой практикой. Исключения в .Net медленны. Всегда гораздо лучше делать собственные проверки, а не использовать исключения, чтобы ловить "плохие" значения, такие как "Нули", как упоминал Джон B .
Я узнал из первых рук в приложении .Net 1.1, которое создало текстовые файлы с разделителями лабораторных данных. Я использовал исключения для улавливания полей с недопустимыми данными, но как только я заменил исключения соответствующим кодом проверки, приложение ускорилось экспоненциально.
Ответ 6
Ключ находится в терминах "Исключение".
Если аргумент null является допустимым бизнес-условием, то по определению пустой аргумент не является исключением, а должен быть протестирован до его использования. Использование исключений для логики управляющей программы в таких бизнес-условиях является неаккуратным кодированием, и преступник должен ударяться несколько раз мокрой пикшей, пока он не раскается.
Тем не менее, производительность в стороне, всегда слепо тестируя в этом примере нулевые аргументы, не лучше, чем всегда захватывать исключение при броске.
Если значение аргумента не должно быть равным нулю, тестирование для null не должно выполняться - за исключением самого верхнего уровня приложения, где необработанные исключения захватываются, сохраняются, отправляются по электронной почте на команды поддержки и скрытые от пользователя, которому должно быть отображено подходящее сообщение, извиняясь.
Мне приходилось отлаживать приложение, в котором было обработано все мыслимые проклятые исключения, и его было невозможно отладить. Его нужно было вытащить из производства, в dev envt вместе с его базой данных, и все обработчики вырвались наружу. После этого было тривиально.
Ответ 7
Ввод тестового блока в С++ - это дорогостоящая операция (с точки зрения циклов процессора), поэтому вы должны свести к минимуму их использование по этой причине. Для С# вход в блок try дешев, поэтому мы можем использовать его по-другому.
Теперь; исключения на С# являются дорогостоящими и по-прежнему должны использоваться для исключительных случаев, а не для общей программной логики, как уже заявили другие...
Ответ 8
C/С++ может быть столь же реактивным. Сколько раз вы видели что-то такое, как fopen, за которым следует NULL-проверка? Преимущество для исключений состоит в том, что они позволяют предоставлять больше информации, чем NULL или False return.
Большинство операций, которые генерируют исключения на любом языке, который их поддерживает, не позволяют "проверить" перед собой, чтобы убедиться, что операция будет успешной.
Теперь исключения во время выполнения - это все вместе разные. Обычно они вызваны недействительными данными или неправильным использованием данных (деление на ноль). Подобные исключения, по моему мнению, никогда не должны быть пойманы как способ борьбы с ними.
Ответ 9
Трудно обсуждать наилучшее использование исключений без общего обсуждения стратегии исключения. Например, ваша стратегия может быть любой из следующих:
- обрабатывать все исключения как можно ближе к точке отказа
- запишите исключение, затем верните вызов вызывающему абоненту
- откат к состоянию предварительного исключения и попытка продолжить
- перевести исключение в соответствующее сообщение об ошибке и отобразить пользователю
Обработка исключений часто может быть осложнена тем фактом, что несколько разработчиков, работающих в одном проекте, могут даже не иметь одинаковую стратегию или, что еще хуже, даже не знают, что они существуют. Поэтому важно, чтобы все в команде знали и понимали, что такое стратегия.
Для хорошей отправной точки по обработке исключений и стратегии см. сообщение в блоге Ned Batchelder Исключения в Rainforest.