Урок 33. Создание исключений в C#


Тридцать третий урок учебника по основам C# завершает исследование обработки исключений. В этой статье мы рассмотрим создание (выбрасывание) исключений для сообщения об ошибках. Это включает использование стандартных и пользовательских типов исключений.

Зачем создавать исключения?

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

Исключения следует создавать только при возникновении непредвиденного или недопустимого действия, которое не позволяет методу выполнить свою обычную функцию; обработка исключений не должна использоваться для нормального потока программы вместо условной обработки. Может быть трудно поддерживать код, который неправильно использует обработку исключений.

Хорошая практика создания исключений включает в себя:

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

Чтобы вызвать исключение, вы используете команду throw. Синтаксис выглядит следующим образом:

throw исключение;
Исключение - это объект исключения, содержащий сведения о возникшей ошибке. Он может быть объявлено как объект и инициализирован перед командой throw или включен в инструкцию throw, используя ключевое слово new и один из конструкторов исключения для задания свойств объекта.

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

using System;
 
namespace BlackWasp
{
    class TestApp
    {
        static void Main(string[] args)
        {
            if (args.Length == 0)
            {
                throw new ArgumentException("A start-up parameter is required.");
            }
 
            Console.WriteLine("{0} argument(s) provided", args.Length);
        }
    }
}
При запуске с параметром программа запускается без ошибок и выводит количество предоставленных параметров. Если аргументы не указаны, тело инструкции if создает исключение. Это позволяет избежать непредвиденных исключений, возникающих позже из-за недопустимых параметров.

Примечание: класс объекта исключения, который нужно бросить, должен соответствовать условию ошибки. Каждый объект исключения имеет конструкторы, которые могут отличаться от показанных выше. Дополнительные сведения о других стандартных классах исключений смотри на сайте MSDN.

Повторное выбрасывание исключений

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

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

throw;
В следующем примере показано, как используется этот синтаксис. Для простоты вызываемые методы не определены, поэтому этот код не может быть выполнен напрямую.

static void Main(string[] args)
{
    try
    {
        InitialiseData(args[0]);
        SaveToDisk();
    }
    catch (Exception ex)
    {
        // Логируем и повторяем
        LogException();
        throw;        
    }
	
Свойство InnerException

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

В следующем примере расширяется пример re-throw выше путем создания FileNotFoundException с новым сообщением об ошибке и сведения о перехваченном исключении в свойстве InnerException.

static void Main(string[] args)
{
    try
    {
        InitialiseData(args[0]);
        SaveToDisk();
    }
    catch (Exception ex)
    {
        LogException();
        throw new System.IO.FileNotFoundException("Invalid file argument", ex);
    }
}
Примечание: код выше приведен в качестве примера установки свойства InnerException. Это не очень хорошая практика, чтобы использовать catch-all и бросить новое исключение, так как это может замаскировать серьезные проблемы.

Пользовательские исключения

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

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

Определение пользовательского исключения

Наше пользовательское исключение будет сообщать об исключениях при печати страницы с полями, которые недопустимы для выбранного принтера. В соответствии с соглашениями об именах новый класс будет называться InvalidPrinterMarginsException. Чтобы определить новый класс в текущем пространстве имен и указать, что он является производным от ApplicationException, используйте следующий код:

class InvalidPrinterMarginsException : ApplicationException
{
 
}
Класс ApplicationException предоставляет три основных конструктора. Первый не требует параметров и создает исключение со значениями свойств по умолчанию. Второй принимает строковый параметр, содержащий сообщение об ошибке. Конечный конструктор позволяет указать сообщение и внутренний объект исключения. Эти три конструктора не наследуются автоматически новым типом исключения, поэтому их следует добавить следующим образом:

class InvalidPrinterMarginsException : ApplicationException
{
    public InvalidPrinterMarginsException() : base() {}
    public InvalidPrinterMarginsException(string s) : base(s) {}
    public InvalidPrinterMarginsException(string s, Exception ex) : base(s, ex) {}
}
Созданиие перехват пользовательского исключения

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

В последнем примере пользовательское исключение сочетается с кодом консольного приложения. Эта программа создает пользовательское исключение в блоке try. Сообщение об исключении выводится следующим блоком catch.

using System;
 
namespace BlackWasp
{
    class TestApp
    {
        static void Main(string[] args)
        {
            try
            {
                throw new InvalidPrinterMarginsException ("The margins are too small");
            }
            catch (InvalidPrinterMarginsException ex)
            {
                Console.WriteLine(ex.Message);
            }
        }
    }
 
    class InvalidPrinterMarginsException : ApplicationException
    {        public InvalidPrinterMarginsException() : base() {}
        public InvalidPrinterMarginsException(string s) : base(s) {}
        public InvalidPrinterMarginsException(string s, Exception ex) : base(s, ex) {}
    }
}
Заключительное замечание по ApplicationException

Когда Microsoft создала платформу .NET framework, они определили два базовых класса. Это были системные исключения (SystemException) и пользовательские исключения (ApplicationException). При рассмотрении этой сегрегации Microsoft поняла, что преимущества такой структуры не были ожидаемыми. По этой причине Корпорация Майкрософт теперь рекомендует создавать пользовательские исключения непосредственно из класса Exception. Это делает иерархию исключений более плоской. Исключение ApplicationException остается частью платформы .NET framework для обеспечения обратной совместимости на момент написания статьи.
Автор этого материала - я - Пахолков Юрий. Я оказываю услуги по написанию программ на языках Java, C++, C# (а также консультирую по ним) и созданию сайтов. Работаю с сайтами на CMS OpenCart, WordPress, ModX и самописными. Кроме этого, работаю напрямую с JavaScript, PHP, CSS, HTML - то есть могу доработать ваш сайт или помочь с веб-программированием. Пишите сюда.

тегистатьи IT, уроки по си шарп, си шарп, исключения, ошибки




Отправляя сообщение я подтверждаю, что ознакомлен и согласен с политикой конфиденциальности данного сайта.




Урок 17. Контроллеры и получение данных от пользователя
Особенности Java
Урок 9. Ожидание завершения параллельных задач C#