(Долго читатель SO, первый раз задающий q.
Я новичок в С#, который был в мире PHP/Ruby/Python уже много лет, поэтому я приношу свои извинения, если это вопрос допинга.)
Я выполняю некоторое обслуживание старого приложения на С#, которое сбой, когда SmtpClient.Send() терпит неудачу. Из того, что я почерпнул из MSDN, я вижу очевидный способ исправить это, но мой вопрос также касается более общего случая.
В соответствии с MSDN:
try {
client.Send(message);
}
catch (Exception ex) {
Console.WriteLine("Exception caught in CreateTestMessage2(): {0}",
ex.ToString() );
}
Все это имеет смысл для меня, но я всегда думал, что всякий раз, когда вы можете предотвратить возможность ошибки, вы это делаете. Есть ли что-нибудь, что вы можете (и должны?) Сделать здесь, чтобы уменьшить вероятность исключений метаданных Send()?
Я предполагаю, что есть ситуации, когда невозможно предотвратить возможность исключения, поэтому вам нужно справиться с этим, но есть ли общий стиль руководства или правила, которые люди используют для их руководства?
Снова, извините, если это вопрос дерьма. Я попытался найти SO и Google, насколько мог.
EDIT: я только что нашел этот вопрос Рекомендации по управлению исключениями в java или C которые вполне могут пойти, чтобы ответить на мой вопрос.
EDIT2: Спасибо за оперативную обратную связь, невероятно быстро. Я уже много думал об этой проблеме и, возможно, это могло бы уточнить то, что я прошу.
Можно ли сказать, что некоторых исключений, таких как SmtpException, действительно нельзя избежать? Правильнее ли говорить о том, что правильный стиль использует исключения, такие как SmtpException, чтобы сказать вам, что что-то пошло не так с передачей, и вы просто обрабатываете его, как бы вы хотели?
Я чувствую, что в этом вопросе я немного тускнею, но я спрашиваю, потому что все, что я могу узнать, полезно для моей уверенности.
При перехвате исключений будьте как можно более конкретными, если знаете, какие исключения следует ожидать от вызванного метода. Это позволит, например, такие ошибки, как OutOfMemoryException, продолжать пузыриться в стеке, где он должен быть необработанным, а затем ваше приложение будет работать быстро (что, вероятно, хорошо, потому что ваша система сейчас находится в неизвестном состоянии, и вы не должны продолжайте).
Существуют разные школы мысли об этом; в некоторых случаях (скажем, вы являетесь сервисом NT) вам нужна высокая доступность вашего приложения и сбои в производстве, потому что вы получаете NullPointerException на каком-то непредвиденном пути кода, может быть нежелательным, если это исключение регистрируется, и у вас есть возможность затем выдайте QFE (не говоря уже о пересмотре вашего тестового режима). Если вы являетесь консольным или формным приложением, то восстановление - это другая история, поскольку исключение может быть всплыло для пользователя, и они могут в интерактивном режиме решить, что такое соответствующее действие.
Мой общий совет: поймайте определенные исключения как можно ближе к источнику, пусть все остальное забьет стек и запустит его в том месте, где у вас достаточно контекста, чтобы позже попытаться воспроизвести исключение. Будьте осторожны, что ловли, а затем повторное бросание дорого; сценарий в вашем примере, где вы, возможно, захотите сделать это, будет, если, скажем, SmtpException был таймаутом соединения (я делаю это), тогда одна стратегия может быть экспоненциальной отступлением и повторять до n раз с момента отправки сервер может быть опущен, а затем, наконец, сдаться и реконструировать, если вам это не удалось.
На самом деле короткий ответ: "Все зависит".
Это хороший пример того, что я называю "экзогенной" ситуацией исключения.
http://ericlippert.com/2008/09/10/vexing-exceptions/
Рассмотрим аналогичный случай - открытие файла. Вы можете уменьшить вероятность получения исключения:
Вы можете сделать все это, а STILL получить исключение при открытии файла. Поскольку между выполнением всех этих проверок и попыткой открыть файл, что-то могло измениться. Другой процесс, возможно, изменил разрешения или заблокировал файл, или пользователь удалил CD-ROM с диска или что-то еще. Является ли действие успешным или нет, основано на экзогенном, реальном состоянии, которое вы не можете проверить каким-либо гарантированным способом.
Экзогенные исключения - это боль, с которыми приходится иметь дело, потому что вам нужно справляться с ними, даже если вы делаете много работы для их устранения.
Отправка почты осуществляется одинаково. Вы можете проверить всевозможные вещи, чтобы попытаться устранить исключение, которое, вероятно, будет работать в 99% случаев, но вы по-прежнему не можете ГАРАНТИРОВАТЬ, что между всеми вашими чеками и фактической попыткой отправить почту, что кто-то не отключил маршрутизатор в точно неправильный момент. Вам просто придется всасывать его и обрабатывать исключения.
При чтении MSDN вы можете увидеть список исключений, созданных методом .Send() и сокращенными причинами, по которым они будут выбрасываться. Проверка на них и обращение с ними до вызова Send() может помочь избежать их, но документация не обязательно охватывает все возможные исключения или причины, по которым они могут произойти.
Вы на правильном пути; и вы правы, что хотите избежать создания исключений, если это возможно. Если по какой-либо другой причине они достаточно дороги для среды выполнения, чтобы создавать и обрабатывать - и если они будут удалены, даже простое исключение может привести к сбою всего приложения.
Исключения не так уж плохи как таковые. Они являются частью стратегии защитного программирования. Вы получите ценные знания от исключения, вызванного причинами сбоя отправки. Которые вы сможете использовать для устранения проблем.
Это вполне нормально, чтобы вызывать исключение, когда возникает исключительная ошибка, поэтому нет ничего плохого в вашем коде. Если SMTP-сервер выключен или адрес электронной почты недействителен или какая-либо другая ошибка сделана, вы хотите, чтобы он выдавал, а не терпел неудачу.
Если вы хотите, чтобы блок try, непосредственно окружающий Send, зависел от того, есть ли что-нибудь для вас здесь. В противном случае вы можете также позволить этому пузырьку до самого блока try.