Создание собственных классов исключений в C#

При разработке программного обеспечения в C# и не только, довольно часто встроенных типов исключений бывает не достаточно. Например, при взаимодействии с каким-либо API web-сервиса вам может понадобиться не только предоставить пользователю сообщение об ошибке, но и передать её код, в соответствии с документацией по API сервиса, какую-то служебную информацию и так далее. И здесь нам уже вряд ли хватит стандартных возможностей того же класса Exception — потребуется создать собственный класс исключений, который будет «заточен» на работу, например, с конкретными API. О том, как создавать собственные классы исключений мы сегодня и поговорим.

Пример создания собственного класса исключений

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

class Program
{
    static void Main(string[] args)
    {
        try
        {
            Person p = new Person { Name = "Tom", Age = 17 };
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Ошибка: {ex.Message}");
        }
        Console.Read();
    }
}
class Person
{
    private int age;
    public string Name { get; set; }
    public int Age
    {
        get { return age; }
        set
        {
            if (value < 18)
            {
                throw new Exception("Лицам до 18 регистрация запрещена");
            }
            else
            {
                age = value;
            }
        }
    }
}

Здесь мы при задании значения свойству Age в классе Person проводим проверку и, если возраст менее 18 лет, то генерируем исключение с использованием класса Exception. Класс Exception в конструкторе принимает строку-сообщение. В основной программе происходит перехват и обработка исключения.

Технически, в представленном коде нет ошибок и цель достигнута — как только возраст оказывается менее 18 лет, то сразу генерируется исключение. Однако, в таких случаях удобнее использовать собственный класс исключений. Ответ на вопрос «Почему?» окажется на поверхности, если попытаться ответить на такой вопрос: что будет с кодом нашей программы, если мы захотим ограничить возраст не 18-ю годами, а, скажем, 40? Как минимум, нам придётся менять сообщение об ошибке. А что, если исключение будет генерироваться не в одном, а в 100 местах в программе? Собственный класс исключений позволяет нам в этом случае:
  1. сделать код программы более универсальным и повысить его поддержку;
  2. сделать код программы более удобным для других разработчиков, так как, согласитесь, что тип исключений, например, AgeException говорит нам об ошибке больше, чем просто Exception, и обработать такое исключение в блоке catch будет более удобно

Таким образом, собственный класс исключений можно создать таким:

class AgeException : Exception
{
    public AgeException(string message): base(message)
    { }
}

По сути, этот класс сейчас ничем, кроме названия не отличается от базового Exception. Однако теперь мы можем создать собственные свойства у этого класса, или переопределять уже унаследованные. Например,

class AgeException : Exception
{
    public int Value { get;}
    public AgeException (string message, int val)
        : base(message)
    {
        Value = val;
    }
}

В свойстве Value мы можем хранить, например, значение возраста и, при необходимости, его получать.

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

class AgeException : ArgumentOutOfRangeException
{
    public int Value { get;}
    public AgeException (string message, int val)
        : base(message)
    {
        Value = val;
    }
}

и получить возможность переопределять уже доступные виртуальные свойства и методы класса ArgumentOutOfRangeException. Соответственно, теперь мы можем сделать наш класс Person более наглядным, например, таким:

    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                Person p = new Person { Name = "Tom", Age = 17 };
            }
            catch (AgeException ex)
            {
                Console.WriteLine("Ошибка: " + ex.Message);
            }
            Console.Read();
        }
    }

    class Person
    {
        public string Name;
        private int age;
        public int Age
        {
            get { return age; }
            set
            {
                if (value < 18)
                    throw new AgeException(value, 18);
                else
                    age = value;
            }
        }
    }

    class AgeException : Exception
    {
        public int value;
        public AgeException(int value, int minValue) : base($"Лицам до {minValue} регистрация запрещена. Указан возраст {value}")
        {
            this.value = value;
        }
    }
}

В этом примере мы уже генерируем собственное исключение типа AgeException в конструктор которого передаем два числа — возраст, который мы пробовали указать в классе Person и минимально допустимый возраст, то есть 18 лет. Исходя из полученных значений мы создаем строку сообщения, которую и передаем в конструктор базового класса.

Итого

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

Подписаться
Уведомить о
guest
0 Комментарий
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии