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


Обработка исключений (руководство по программированию на C#)

Блок try используется программистами C# для разделения кода, который может быть затронут исключением. Связанные блоки перехвата используются для обработки всех результирующих исключений. Finally блок содержит код, который выполняется независимо от того, возникает ли исключение в блоке try, например, освобождение ресурсов, выделенных в блоке try. Для try блока требуется один или несколько связанных catch блоков, или finally блок или оба блока.

В следующих примерах показаны оператор try-catch, оператор try-finally и оператор try-catch-finally.

try
{
    // Code to try goes here.
}
catch (SomeSpecificException ex)
{
    // Code to handle the exception goes here.
    // Only catch exceptions that you know how to handle.
    // Never catch base class System.Exception without
    // rethrowing it at the end of the catch block.
}
try
{
    // Code to try goes here.
}
finally
{
    // Code to execute after the try block goes here.
}
try
{
    // Code to try goes here.
}
catch (SomeSpecificException ex)
{
    // Code to handle the exception goes here.
}
finally
{
    // Code to execute after the try (and possibly catch) blocks
    // goes here.
}

Блок try, не имеющий блока catch или блока finally, вызывает ошибку компилятора.

Перехват блоков

Блок catch может указать тип исключения для перехвата. Спецификация типа называется фильтром исключений. Тип исключения должен быть производным от Exception. Как правило, не указывайте Exception в качестве фильтра исключений, если вы не знаете, как обрабатывать все исключения, которые могут быть вызваны в try блоке, или если вы добавили инструкцию throw в конце блока catch.

Несколько catch блоков с разными классами исключений можно объединить. Блоки catch вычисляются сверху вниз в коде, но для каждого исключения, которое создается, выполняется только один catch блок. catch Первый блок, указывающий точный тип или базовый класс создаваемого исключения, выполняется. Если ни один блок catch не указывает соответствующий класс исключений, будет выбран блок catch, который не имеет типа, если такой блок присутствует в инструкции. Важно сначала разместить catch блоки с наиболее конкретными классами исключений (т. е. наиболее производными).

Перехватывайте исключения, если выполняются следующие условия:

  • У вас есть хорошее представление о том, почему исключение может быть создано, и вы можете реализовать определенное восстановление, например предложить пользователю ввести новое имя файла при перехвате FileNotFoundException объекта.
  • Вы можете создать и бросить новое, более конкретное исключение.
    int GetInt(int[] array, int index)
    {
        try
        {
            return array[index];
        }
        catch (IndexOutOfRangeException e)
        {
            throw new ArgumentOutOfRangeException(
                "Parameter index is out of range.", e);
        }
    }
    
  • Вы хотите частично обработать исключение перед его передачей для дальнейшей обработки. В следующем примере блок catch используется для добавления записи в журнал ошибок перед перебросом исключения.
    try
    {
        // Try to access a resource.
    }
    catch (UnauthorizedAccessException e)
    {
        // Call a custom error logging procedure.
        LogError(e);
        // Re-throw the error.
        throw;
    }
    

Можно также указать фильтры исключений , чтобы добавить логическое выражение в предложение catch. Фильтры исключений указывают, что конструкция catch срабатывает только в том случае, если условие истинно. В следующем примере оба предложения catch используют один и тот же класс исключений, но для создания другого сообщения об ошибке проверяется дополнительное условие:

int GetInt(int[] array, int index)
{
    try
    {
        return array[index];
    }
    catch (IndexOutOfRangeException e) when (index < 0) 
    {
        throw new ArgumentOutOfRangeException(
            "Parameter index cannot be negative.", e);
    }
    catch (IndexOutOfRangeException e)
    {
        throw new ArgumentOutOfRangeException(
            "Parameter index cannot be greater than the array size.", e);
    }
}

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

public class ExceptionFilter
{
    public static void Main()
    {
        try
        {
            string? s = null;
            Console.WriteLine(s.Length);
        }
        catch (Exception e) when (LogException(e))
        {
        }
        Console.WriteLine("Exception must have been handled");
    }

    private static bool LogException(Exception e)
    {
        Console.WriteLine($"\tIn the log routine. Caught {e.GetType()}");
        Console.WriteLine($"\tMessage: {e.Message}");
        return false;
    }
}

Метод LogException всегда возвращает false, ни одно предложение с использованием этого фильтра исключений не соответствует. Предложение catch может быть общим, используя System.Exception, и последующие предложения могут обрабатывать более конкретные классы исключений.

Наконец, блоки

Блок finally позволяет очистить действия, выполняемые в блоке try . Если блок finally присутствует, он выполняется последним, после блока try и любого соответствующего блока catch. finally блок всегда выполняется: возникает ли исключение или обнаружен catch блок, соответствующий типу исключения.

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

В следующем примере finally блок используется для закрытия файла, открываемого в блоке try . Обратите внимание, что состояние дескриптора файла проверяется до закрытия файла. try Если блок не может открыть файл, дескриптор файла по-прежнему имеет значениеnull, и finally блок не пытается закрыть его. Вместо этого, если файл успешно открыт в блоке try , finally блок закрывает открытый файл.

FileStream? file = null;
FileInfo fileinfo = new System.IO.FileInfo("./file.txt");
try
{
    file = fileinfo.OpenWrite();
    file.WriteByte(0xF);
}
finally
{
    // Check for null because OpenWrite might have failed.
    file?.Close();
}

Спецификация языка C#

Дополнительные сведения см. в разделе "Исключения " и инструкции try в спецификации языка C#. Спецификация языка является авторитетным источником синтаксиса и использования языка C#.

См. также