- Лучшие методики обработки исключений Best practices for exceptions
- Использование блоков try/catch/finally для восстановления после ошибок или высвобождения ресурсов Use try/catch/finally blocks to recover from errors or release resources
- Обработка общих условий без выдачи исключений Handle common conditions without throwing exceptions
- Устранение исключений при разработке классов Design classes so that exceptions can be avoided
- Выдача исключений вместо возврата кода ошибки Throw exceptions instead of returning an error code
- Использование предопределенных типов исключений .NET Use the predefined .NET exception types
- Завершайте имена классов исключений словом Exception End exception class names with the word Exception
- Включение трех конструкторов в пользовательские классы исключений Include three constructors in custom exception classes
- Обеспечение доступности данных об исключении при удаленном выполнении кода Ensure that exception data is available when code executes remotely
- Использование грамматически правильных сообщений об ошибке Use grammatically correct error messages
- Включение локализованной строки сообщения в каждое исключение Include a localized string message in every exception
- Предоставление дополнительных свойств в пользовательских исключениях по мере необходимости In custom exceptions, provide additional properties as needed
- Размещение операторов throw для удобной трассировки стека Place throw statements so that the stack trace will be helpful
- Использование методов построителя исключений Use exception builder methods
- Восстановление состояния, если методы не выполняются из-за исключения Restore state when methods don’t complete due to exceptions
Лучшие методики обработки исключений Best practices for exceptions
Хорошо спроектированное приложение обрабатывает исключения и ошибки, чтобы предотвратить сбои приложения. A well-designed app handles exceptions and errors to prevent app crashes. В этом разделе описываются рекомендации по обработке и созданию исключений. This section describes best practices for handling and creating exceptions.
Использование блоков try/catch/finally для восстановления после ошибок или высвобождения ресурсов Use try/catch/finally blocks to recover from errors or release resources
Используйте блоки try / catch , выделив с их помощью код, который потенциально может явиться источником исключения, таким образом можно будет выполнить восстановление кода после возникновения этого исключения. Use try / catch blocks around code that can potentially generate an exception and your code can recover from that exception. В блоках catch следует всегда упорядочивать исключения от более производных к менее производным. In catch blocks, always order exceptions from the most derived to the least derived. Все исключения, производные от Exception. All exceptions derive from Exception. Более производные исключения не обрабатываются предложением catch, которому предшествует предложение catch для базового класса исключения. More derived exceptions are not handled by a catch clause that is preceded by a catch clause for a base exception class. Если ваш код не удается восстановить после возникновения исключения, не перехватывайте это исключение. When your code cannot recover from an exception, don’t catch that exception. Включите методы выше по стеку вызовов для восстановления по мере возможности. Enable methods further up the call stack to recover if possible.
Очистите ресурсы, выделенные с помощью инструкций using или блоков finally . Clean up resources allocated with either using statements, or finally blocks. Рекомендуется использовать инструкции using для автоматической очистки ресурсов при возникновении исключений. Prefer using statements to automatically clean up resources when exceptions are thrown. Используйте блоки finally , чтобы очистить ресурсы, которые не реализуют IDisposable. Use finally blocks to clean up resources that don’t implement IDisposable. Код в предложении finally выполняется почти всегда — даже при возникновении исключений. Code in a finally clause is almost always executed even when exceptions are thrown.
Обработка общих условий без выдачи исключений Handle common conditions without throwing exceptions
Для условий, которые могут возникнуть, но способны вызвать исключение, рекомендуется реализовать обработку таким способом, который позволит избежать исключения. For conditions that are likely to occur but might trigger an exception, consider handling them in a way that will avoid the exception. Например, при попытке закрыть уже закрытое подключение возникает InvalidOperationException . For example, if you try to close a connection that is already closed, you’ll get an InvalidOperationException . Этого можно избежать, используя оператор if для проверки состояния подключения перед попыткой закрыть его. You can avoid that by using an if statement to check the connection state before trying to close it.
Если состояние подключения перед закрытием не проверяется, исключение InvalidOperationException можно перехватить. If you don’t check connection state before closing, you can catch the InvalidOperationException exception.
Выбор конкретного способа зависит от того, насколько часто ожидается возникновение данного события. The method to choose depends on how often you expect the event to occur.
Используйте обработку исключений, если событие не происходит очень часто, то есть если событие носит действительно исключительный характер и указывает на ошибку (например, в случае неожиданного конца файла). Use exception handling if the event doesn’t occur very often, that is, if the event is truly exceptional and indicates an error (such as an unexpected end-of-file). При использовании обработки исключений в обычных условиях выполняется меньше кода. When you use exception handling, less code is executed in normal conditions.
Если событие происходит регулярно в рамках нормальной работы программы, выполняйте проверку на наличие ошибок прямо в коде. Check for error conditions in code if the event happens routinely and could be considered part of normal execution. Проверка на наличие распространенных условий ошибки позволяет выполнять меньший объем кода благодаря устранению исключений. When you check for common error conditions, less code is executed because you avoid exceptions.
Устранение исключений при разработке классов Design classes so that exceptions can be avoided
Класс может предоставлять методы и свойства, позволяющие избежать вызова, способного выдать исключение. A class can provide methods or properties that enable you to avoid making a call that would trigger an exception. Например, класс FileStream содержит методы, позволяющие определить, достигнут ли конец файла. For example, a FileStream class provides methods that help determine whether the end of the file has been reached. Это позволяет избежать появления исключения, создаваемого в случае выполнения чтения после окончания файла. These can be used to avoid the exception that is thrown if you read past the end of the file. В следующем примере показан способ чтения до конца файла без выдачи исключения. The following example shows how to read to the end of a file without triggering an exception.
Другой способ устранения исключений заключается в том, что для наиболее общих и часто встречающихся ошибок следует возвращать значение NULL (или значение по умолчанию). Another way to avoid exceptions is to return null (or default) for extremely common error cases instead of throwing an exception. Такие ошибки могут относиться к обычному потоку управления. An extremely common error case can be considered normal flow of control. Возвращая значение NULL (или значение по умолчанию) в таких случаях, можно уменьшить влияние на производительность приложения. By returning null (or default) in these cases, you minimize the performance impact to an app.
При выборе типа значения Nullable или значения по умолчанию в качестве индикатора ошибки учитывайте особенности приложения. For value types, whether to use Nullable or default as your error indicator is something to consider for your particular app. При использовании Nullable «default принимает значение null , а не Guid.Empty . By using Nullable , default becomes null instead of Guid.Empty . В некоторых случаях добавление Nullable помогает более точно определить, присутствует или отсутствует значение. Some times, adding Nullable can make it clearer when a value is present or absent. Но в определенных ситуациях добавление Nullable может привести к созданию лишних необязательных случаев для проверки, что повышает вероятность ошибки. Other times, adding Nullable can create extra cases to check that aren’t necessary, and only serve to create potential sources of errors.
Выдача исключений вместо возврата кода ошибки Throw exceptions instead of returning an error code
Исключения гарантируют, что сбои не останутся незамеченными из-за того, что вызывающий код не проверил код возврата. Exceptions ensure that failures do not go unnoticed because calling code didn’t check a return code.
Использование предопределенных типов исключений .NET Use the predefined .NET exception types
Создавайте новый класс исключений, только если предопределенное исключение не подходит. Introduce a new exception class only when a predefined one doesn’t apply. Пример: For example:
Вызывайте исключение InvalidOperationException, если значение свойства или вызов метода не соответствуют текущему состоянию объекта. Throw an InvalidOperationException exception if a property set or method call is not appropriate given the object’s current state.
Порождайте исключение ArgumentException или одного из предварительно определенных классов, которые являются производными от ArgumentException, если передаются недопустимые параметры. Throw an ArgumentException exception or one of the predefined classes that derive from ArgumentException if invalid parameters are passed.
Завершайте имена классов исключений словом Exception End exception class names with the word Exception
Если требуется пользовательское исключение, присвойте ему соответствующее имя и сделайте его производным от класса Exception. When a custom exception is necessary, name it appropriately and derive it from the Exception class. Пример: For example:
Включение трех конструкторов в пользовательские классы исключений Include three constructors in custom exception classes
При создании собственных классов исключений можно использовать по меньшей мере три общих конструктора: конструктор без параметров, конструктор, принимающий строковое сообщение, и конструктор, принимающий строковое сообщение и внутреннее исключение. Use at least the three common constructors when creating your own exception classes: the parameterless constructor, a constructor that takes a string message, and a constructor that takes a string message and an inner exception.
Exception(), использующий значения по умолчанию. Exception(), which uses default values.
Exception(String), принимающий строковое сообщение. Exception(String), which accepts a string message.
Exception(String, Exception), принимающий строковое сообщение и внутреннее исключение. Exception(String, Exception), which accepts a string message and an inner exception.
Обеспечение доступности данных об исключении при удаленном выполнении кода Ensure that exception data is available when code executes remotely
При создании пользовательских исключений следует обеспечить доступность метаданных исключений для удаленно исполняемого кода. When you create user-defined exceptions, ensure that the metadata for the exceptions is available to code that is executing remotely.
Например, для реализаций .NET, которые поддерживают домены приложений, могут возникать исключения для этих доменов. For example, on .NET implementations that support App Domains, exceptions may occur across App domains. Предположим, что домен приложения А создает домен приложения В, который выполняет код, вызывающий исключение. Suppose App Domain A creates App Domain B, which executes code that throws an exception. Чтобы домен приложения A правильно перехватил и обработал исключение, он должен найти сборку, которая содержит исключение, порожденное доменом приложения B. Если домен приложения B порождает исключение, содержащееся в сборке в его базовой папке приложения, но не в базовой папке приложения домена A, то домен приложения A не сможет найти исключение и среда CLR породит исключение FileNotFoundException. For App Domain A to properly catch and handle the exception, it must be able to find the assembly that contains the exception thrown by App Domain B. If App Domain B throws an exception that is contained in an assembly under its application base, but not under App Domain A’s application base, App Domain A will not be able to find the exception, and the common language runtime will throw a FileNotFoundException exception. Чтобы избежать такой ситуации, можно развернуть сборку, содержащую сведения об исключении, двумя способами: To avoid this situation, you can deploy the assembly that contains the exception information in two ways:
Поместите эту сборку в общую базу приложения, совместно используемую обоими доменами приложений. Put the assembly into a common application base shared by both app domains.
Если у этих доменов нет общей базы приложения, то подпишите сборку, содержащую сведения об исключении, строгим именем и разверните ее в глобальном кэше сборок. If the domains do not share a common application base, sign the assembly that contains the exception information with a strong name and deploy the assembly into the global assembly cache.
Использование грамматически правильных сообщений об ошибке Use grammatically correct error messages
Составляйте понятные предложения, указывая в конце знаки препинания. Write clear sentences and include ending punctuation. Каждое предложение в строке, назначенной свойству Exception.Message, должно заканчиваться точкой. Each sentence in the string assigned to the Exception.Message property should end in a period. Например, «Таблица журнала переполнена.» For example, «The log table has overflowed.» будет подходящей строкой сообщения. would be an appropriate message string.
Включение локализованной строки сообщения в каждое исключение Include a localized string message in every exception
Сообщение об ошибке, показываемое пользователю, извлекается из свойства Exception.Message созданного исключения, а не из имени класса исключения. The error message that the user sees is derived from the Exception.Message property of the exception that was thrown, and not from the name of the exception class. Как правило, вы присваиваете значение свойству Exception.Message, передав строку сообщения аргументу message конструктора исключений. Typically, you assign a value to the Exception.Message property by passing the message string to the message argument of an Exception constructor.
Для локализованных приложений необходимо предоставить строку локализованного сообщения для всех исключений, которые может создавать приложение. For localized applications, you should provide a localized message string for every exception that your application can throw. Используйте файлы ресурсов для предоставления локализованных сообщений об ошибках. You use resource files to provide localized error messages. Сведения о локализации приложений и извлечении локализованных строк см. в следующих статьях: For information on localizing applications and retrieving localized strings, see the following articles:
Предоставление дополнительных свойств в пользовательских исключениях по мере необходимости In custom exceptions, provide additional properties as needed
Дополнительные сведения (кроме строки настраиваемого сообщения) включайте в исключение только в случаях, когда в соответствии со сценарием программирования такие дополнительные сведения могут оказаться полезными. Provide additional properties for an exception (in addition to the custom message string) only when there’s a programmatic scenario where the additional information is useful. Например, исключение FileNotFoundException предоставляет свойство FileName. For example, the FileNotFoundException provides the FileName property.
Размещение операторов throw для удобной трассировки стека Place throw statements so that the stack trace will be helpful
Трассировка стека начинается в операторе, породившем исключение, и завершается оператором catch , перехватывающим это исключение. The stack trace begins at the statement where the exception is thrown and ends at the catch statement that catches the exception.
Использование методов построителя исключений Use exception builder methods
Обычно класс генерирует одно и то же исключение из различных мест своей реализации. It is common for a class to throw the same exception from different places in its implementation. Чтобы избежать повторения кода, используйте вспомогательные методы, создающие исключение и затем возвращающие его. To avoid excessive code, use helper methods that create the exception and return it. Пример: For example:
В некоторых случаях для создания исключения лучше воспользоваться конструктором исключений. In some cases, it’s more appropriate to use the exception’s constructor to build the exception. В качестве примера можно привести класс глобальных исключений, например ArgumentException. An example is a global exception class such as ArgumentException.
Восстановление состояния, если методы не выполняются из-за исключения Restore state when methods don’t complete due to exceptions
Вызывающие объекты должны предполагать, что при создании исключения из метода не возникают побочные эффекты. Callers should be able to assume that there are no side effects when an exception is thrown from a method. Например, если у вас есть код, который передает деньги, списывая их с одного счета и внося на другой, и при начислении средств возникает исключение, списание средств применяться не должно. For example, if you have code that transfers money by withdrawing from one account and depositing in another account, and an exception is thrown while executing the deposit, you don’t want the withdrawal to remain in effect.
Описанный выше метод непосредственно не создает исключения, однако при его написании необходимо соблюдать осторожность, чтобы при сбое операции начисления списание отменялось. The method above does not directly throw any exceptions, but must be written defensively so that if the deposit operation fails, the withdrawal is reversed.
Один из способов обработки в этой ситуации заключается в перехвате всех исключений, выданных транзакцией начисления средств, и откате транзакции списания средств. One way to handle this situation is to catch any exceptions thrown by the deposit transaction and roll back the withdrawal.
В этом примере показано использование throw для повторного порождения исходного исключения. Это позволяет вызывающим объектам проще установить фактическую причину проблемы, не обращаясь к свойству InnerException. This example illustrates the use of throw to re-throw the original exception, which can make it easier for callers to see the real cause of the problem without having to examine the InnerException property. Альтернативным способом является выдача нового исключения с включением исходного исключения в качестве внутреннего: An alternative is to throw a new exception and include the original exception as the inner exception: