Когда я читаю библиотеку классов корпоративного уровня, у меня возникает следующее требование:
- Ваш компонент не должен создавать исключений и нарушать работу системы.
- Он должен корректно обрабатывать ошибки и иметь возможность восстанавливаться после ошибок.
Такие требования хорошо читаются как разработчик, прежде чем ложиться спать. Все сценарии ошибок, даже в ваших самых смелых снах, рано или поздно станут реальностью, и вы просыпаетесь в 3 часа ночи в крике ночи.
Требования звучат хорошо, но их последствия далеко идущие. Если вы пытаетесь перехватить все ошибки в вашем компоненте, вы делаете только скрытие ошибок. Такие системы, как правило, работают медленно, и иногда они просто перестают работать, и никто не знает почему, потому что какая-то последующая ошибка привела систему в состояние, в котором действительно ничего не работает. Печально то, что если вы подключите отладчик к этой устаревшей машине, вы не увидите исходную ошибку, которая вызвала проблему, а последующую проблему, из-за которой вам придется работать обратно к исходной проблеме. Если вы хотите войти на сервер SQL, я могу заверить вас, что у вас будут отсутствовать или отсутствовать журналы довольно часто.
Вам следует подумать о более надежных адресатах журнала (настраиваемый журнал событий, файл журнала, ... или оба). И избегайте асинхронного ведения журнала любой ценой, поскольку необработанное исключение приведет к замедлению процесса намного быстрее, чем вы сможете выйти из него в потоке ведения журнала.
В действительности вам необходимо как можно раньше остановить систему, если произошла фатальная ошибка. Ошибок, из которых вы можете безопасно восстановиться без повреждения состояния, как правило, очень мало, но они стоят усилий, так как вам нужно спроектировать свою библиотеку классов, чтобы она была устойчивой (справлялась с ожидаемыми ошибками) и безопасной (создавала исключения, регистрировала их, ... ).
На этапе проектирования легче придумать идеальную систему, способную справиться со всеми ошибками. Архитекторам программного обеспечения нравятся такие системы. И они могут сойти с рук, потому что на бумаге это выглядит хорошо. Или другими словами: пузыри не разбиваются.
Но во время выполнения ваша библиотека классов использует
- Реальная физическая память (OutOfMemoryException)
- Реальное пространство на жестком диске (по крайней мере, файлы журналов) (жесткий диск заполнен)
- Реальные циклы ЦП (100% ЦП)
- Пространство реального стека (StackOverFlowException)
Если вы попытаетесь скрыть неустранимые ошибки, система станет труднее отлаживать, но она все равно не будет работать, поскольку ваша библиотека будет использовать общие ресурсы, которые не принадлежат исключительно вашему компоненту. Лучше немедленно прекратить работу в таких ситуациях, чем продолжать и позволить другим также потерпеть неудачу.
Идея скрыть все ошибки внутри компонента ошибочна, поскольку вы не можете предотвратить фатальные ошибки внутри вашего компонента, чтобы повлиять на другие компоненты в системе.
Это было сказано, я надеюсь, что вы спрашивали не о том, как перехватить все ошибки на уровне библиотеки классов, а в ваших конечных приложениях, где вы отображаете ошибки для пользователя и регистрируете их (надеюсь, только один раз и не снова на каждом слое, потому что вы не доверяйте другим слоям). Как уже отмечали другие, вы можете получить в свои руки необработанные исключения, которые прервали поток, который, в свою очередь, прервет ваш процесс, когда ваш обработчик ошибок останется. Помимо этого, неплохо бы поместить блок try / catch вокруг метода Main, чтобы перехватывать ошибки в основном потоке. Если ваше приложение имеет пользовательский интерфейс (Windows Forms), вам также следует зарегистрироваться в событии Application.ThreadException, которое будет вызываться, когда исключение распространяется из обработчика сообщений Window.
С уважением,
Алоис Краус