Поделиться через


Лучшие методики обработки исключений

Обновлен: Ноябрь 2007

Хорошо разработанный набор блоков кода обработчиков ошибки может сделать программу более устойчивой и менее подверженной сбоям за счет обработки таких ошибок в приложении. В следующем списке содержатся рекомендации по обработке исключений.

  • Необходимо четко представлять, когда следует устанавливать блок try/catch. Например, можно программным путем проверять условие, которое с большой вероятностью может возникать без обработки исключений. В других ситуациях для перехвата ошибочных условий целесообразнее применить обработку исключений.

    В следующем примере показано использование оператора if для проверки закрытия подключения. Этот метод можно использовать вместо создания исключения в случае, если подключение не закрыто.

       If conn.State <> ConnectionState.Closed Then
          conn.Close()
       End If
    
       if(conn.State != ConnectionState.Closed)
          conn.Close();
    

    В следующем примере в случае, если подключение не закрыто, создается исключение.

       Try
          conn.Close()
       Catch ex As InvalidOperationException
          'Do something with the error or ignore it.
       End Try
    
       try {
         conn.Close();
       }
       catch(InvalidOperationException ex) {
         //Do something with the error or ignore it.
       }
    

    Выбираемый метод зависит от того, насколько часто ожидается возникновение данного события. Если событие носит действительно исключительный характер и представляет собой ошибку (например, неожиданно встретившийся конец файла), то лучше применить обработку исключений, потому что при этом в нормальном состоянии исполняется меньшее количество кода. Если событие встречается регулярно, лучше использовать программную проверку ошибки. В этом случае, если появляется исключение, оно будет обрабатываться дольше.

  • Используйте блоки try/finally, выделив с их помощью тот код, который потенциально может явиться источником исключения, и разместите все операторы catch в одном месте. При таком способе оператор try создает исключение, оператор finally закрывает или освобождает ресурсы, а оператор catch обрабатывает исключение, располагаясь в одном месте.

  • Всегда упорядочивайте исключения в блоках catch, размещая их в порядке убывания определенности. При таком методе определенное исключение обрабатывается до его передачи в блок перехвата более общего исключения.

  • Имена классов исключений завершайте словом "Exception". Пример:

    Public Class EmployeeListNotFoundException
        Inherits Exception
    
    public class MyFileNotFoundException : Exception {
    }
    
  • При создании пользовательских исключений необходимо обеспечить доступность метаданных исключений для кода, исполняемого удаленно, включая и те исключения, которые возникают между доменными приложениями. Например, предположим, что домен приложения А создает домен приложения В, который выполняет код, порождающий исключение. Чтобы домен приложения A правильно перехватил и обработал это исключение, он должен иметь возможность найти сборку, содержащую исключение, вызванное доменом приложения B. Если домен приложения B вызывает исключение, содержащееся в его базе приложения, но не в базе домена приложения A, то домен приложения A не сможет найти исключение и среда CLR создаст исключение FileNotFoundException. Чтобы избежать такой ситуации, можно развернуть сборку, содержащую сведения об исключении, двумя способами.

    • Поместите эту сборку в общую базу приложения, совместно используемую обоими доменами приложений

      либо

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

  • В C# и С++ при создании своих собственных классов исключений используйте по крайней мере три общих конструктора. Пример см. в разделе Практическое руководство. Создание пользовательских исключений.

  • В большинстве случаев используйте предварительно определенные типы исключений. Определяйте новые типы исключений только для программных сценариев. Вводите новый класс исключений, чтобы предоставить программисту возможность выполнить на основе этого класса другие операции с кодом.

  • Для большинства приложений унаследуйте пользовательские исключения от класса Exception. Изначально предполагалось, что пользовательские исключения должны наследовать от класса ApplicationException, однако на практике это не имеет особого значения.

  • В каждое исключение включайте локализованную строку описания. Сообщение об ошибке, отображаемое пользователю, извлекается из строки описания созданного исключения, а не из класса исключения.

  • Используйте грамматически правильные сообщения об ошибке с соответствующей пунктуацией. Каждое предложение в строке описания должно заканчиваться точкой.

  • Обеспечьте программный доступ к свойствам Exception. Дополнительные сведения (кроме строки описания) включайте в исключение только в тех случаях, когда в соответствии со сценарием программирования такие дополнительные сведения могут оказаться полезными.

  • Для наиболее общих и часто встречающихся ошибок следует возвращать пустое значение. Например, метод Open возвращает null, если файл не найден, но создает исключение, если этот файл заблокирован.

  • Разрабатывайте классы таким образом, чтобы исключение никогда не создавалось при нормальном использовании. Например, класс FileStream предоставляет еще одну возможность для определения факта достижения конца файла. Это позволяет избежать создания исключения, создаваемого в случае выполнения чтения после окончания файла. В следующем примере показан способ чтения до конца файла.

    Class FileRead
    
        Public Sub Open(ByVal fileToRead As FileStream)
    
            ' This If statement is optional
            ' as it is very unlikely that
            ' the stream would ever be null
            If IsDBNull(fileToRead) Then
                Throw New System.ArgumentNullException()
            End If
    
            Dim b As Integer
    
            ' Set the stream position to the beginning of the file.
            fileToRead.Seek(0, SeekOrigin.Begin)
    
            ' Read each byte to the end of the file.
            For i As Integer = 0 To fileToRead.Length
                b = fileToRead.ReadByte()
                Console.Write(b.ToString())
                ' Or do something else with the byte.
            Next
        End Sub
    
    End Class
    class FileRead {
        public void Open(FileStream fileToRead) 
        {
    
            // This if statement is optional
            // as it is very unlikely that
            // the stream would ever be null.
            if (fileToRead == null)
            {
                throw new System.ArgumentNullException();
            }
    
            int b;
    
            // Set the stream position to the beginning of the file.
            fileToRead.Seek(0, SeekOrigin.Begin);
    
            // Read each byte to the end of the file.
            for (int i = 0; i < fileToRead.Length; i++)
            {
                b = fileToRead.ReadByte();
                Console.Write(b.ToString());
                // Or do something else with the byte.
            }
        }
    } 
    
  • Создает исключение InvalidOperationException, если значение свойства или вызов метода не соответствуют текущему состоянию объекта.

  • Создает исключение ArgumentException или класс, производный от ArgumentException, если передаются недопустимые параметры.

  • Трассировка стека начинается в операторе, породившем исключение, и завершается оператором catch, перехватывающим это исключение. Не забывайте об этом, принимая решение о месте расположения оператора throw.

  • Используйте методы построителя исключений. Обычно класс генерирует одно и то же исключение из различных мест своей реализации. Чтобы избежать повторения кода, используйте вспомогательные методы, создающие исключение и затем возвращающие его. Пример:

    Class File
       Private fileName As String
    
       Public Function Read(bytes As Integer) As Byte()
          If Not ReadFile(handle, bytes) Then
             Throw NewFileIOException()
          End If
       End Function 'Read
    
       Function NewFileIOException() As FileException
          Dim description As String = __unknown ' Build localized string, including fileName.
          Return New FileException(description) '
       End Function 'NewFileIOException
    End Class 'File
    
    class File {
        string fileName;
        public byte[] Read(int bytes) {
            if (!ReadFile(handle, bytes))
                throw NewFileIOException();
        }
        FileException NewFileIOException() {
            string description = // Build localized string, including fileName.
            return new FileException(description);
         }
    }
    

    Другой вариант — воспользуйтесь конструктором для построения исключения. Это более подходит для глобальных классов исключений, таких как ArgumentException.

  • Вместо возврата кода ошибки или HRESULT следует создавать исключения.

  • Устраняйте промежуточные результаты при порождении исключения. Вызывающие объекты должны предполагать, что при создании исключения из метода побочные эффекты не возникают.

См. также

Другие ресурсы

Обработка и создание исключений