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

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

В предыдущей статье я рассказывал о генерации исключений в программах, написанных на C#. Прочитав её, Вы узнаете для чего предназначен данный механизм и как им пользоваться. А в этой статье я хочу рассказать о том, как создавать свои классы-исключения, т.е. такие классы, объекты которых, можно использовать для генерации исключений оператором throw.

И так, как мы помним, механизм генерации исключений нужен для информирования системы, о том, что в программе произошла некая исключительная ситуация (ошибка) и дальнейшее выполнение программы, без её обработки, невозможно! А объекты-исключения, который как бы «выбрасываются в эфир» оператором throw должны содержать информацию о произошедшей ошибке. И когда Вы разрабатываете свой специфический класс, Вам нужно позаботиться о генерации исключений в нештатных ситуациях, связанных с неправильным использованием этого класса. И вот тут начинается самое интересное…

Как мы помним, объект, который как бы «выбрасывается в эфир» оператором throw должен относиться к классу, который является прямым или косвенным наследником класса «Exception» (из пространства имен «System»), ну или вообще являться объектом этого класса. В примере из предыдущей статьи, мы поступали так:

//Конструктор
public Person(string aName, int anAge)
{
    name = aName;

    //Если указан отрицательный возраст
    if (anAge < 0)
    {
        throw new Exception("Отрицательный возраст");
        //Дальше, конструктор выполняться не будет...
    }

    age = anAge;
}

Строка генерации исключения выделена. Так поступать можно, но не совсем правильно! Почему? Да потому, что при возможности, нужно максимально конкретизировать произошедшую ошибку (чтобы в месте её обработки было легче понять причину возникновения). Это делается как минимум двумя способами:

  • генерацией ошибки (объекта) определенного типа (класса), а не обобщенного, такого как «Exception», что использовался в примере выше;
  • передачей в создаваемый объект-исключение дополнительной информации.

Как это делается на практике? Ну в нашем примере, причиной ошибки служило некорректное значение аргумента, по-этому вместо объекта класса «Exception» мы могли бы использовать объект стандартного класса «ArgumentException» (думаю, название класса, говорит само за себя) из пространства имен «System«. Выглядеть это могло бы так:

//Конструктор
public Person(string aName, int anAge)
{
    name = aName;

    //Если указан отрицательный возраст
    if (anAge < 0)
    {
        throw new ArgumentException("Отрицательный возраст");
        //Дальше, конструктор выполняться не будет...
    }

    age = anAge;
}

Среди стандартных классов C#, есть ряд классов, которые предназначены для описания часто встречаемых в программах ошибок. Одним из таких классов является класс «ArgumentException».

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

//Класс, для описания пользовательского типа ошибок
class PersonException : Exception //Используем наследование
{
    //Принимает сообщение с описание ошибки, и код ошибки
    public PersonException(string aMessage, int aCode)
        : base(aMessage) //Вызываем конструктор базового класса
    {
        errorCode = aCode;
    }

    //Возвращает код ошибки
    public int ErrorCode { get { return errorCode; } }

    //Код ошибки
    private int errorCode;
}

Как не сложно заметить, в примере приведенном выше, мы создали класс наследник класса «Exception» и расширили его полем «errorCode», которое хранит код ошибки. Так же, добавили свойство, возвращающее этот код, и создали конструктор, который принимает описание ошибки и её код. Причем, в конструкторе, мы вызываем конструктор базового класса, которому передаем сообщение об ошибке. Сгенерить исключение такого типа в конструкторе класса «Person» мы могли бы так:

//Конструктор
public Person(string aName, int anAge)
{
    name = aName;

    //Если указан отрицательный возраст
    if (anAge < 0)
    {
        throw new PersonException("Отрицательный возраст", 20);
        //Дальше, конструктор выполняться не будет...
    }

    age = anAge;
}

А обрабатывать так:

try
{
    Person somePerson = new Person("Иван", -21);
}
catch (PersonException ex)
{
    //Обработка ошибок
    Console.WriteLine("Произошла ошибка: " + ex.Message + "; с кодом: " + ex.ErrorCode);
}

Как видите, в блоке catch мы явно указали что он обрабатывает исключения типа «PersonException» и исключения других типов в него не попадут. В следующей статье, я хочу подробнее коснуться этой темы.

Добавить комментарий