Как создать свое исключение c
Если нас не устраивают встроенные типы исключений, то мы можем создать свои типы. Базовым классом для всех исключений является класс Exception, соответственно для создания своих типов мы можем унаследовать данный класс.
Допустим, у нас в программе будет ограничение по возрасту:
В классе Person при установке возраста происходит проверка, и если возраст меньше 18, то выбрасывается исключение. Класс Exception принимает в конструкторе в качестве параметра строку, которое затем передается в его свойство Message.
Но иногда удобнее использовать свои классы исключений. Например, в какой-то ситуации мы хотим обработать определенным образом только те исключения, которые относятся к классу Person. Для этих целей мы можем сделать специальный класс PersonException:
По сути класс кроме пустого конструктора ничего не имеет, и то в конструкторе мы просто обращаемся к конструктору базового класса Exception, передавая в него строку message. Но теперь мы можем изменить класс Person, чтобы он выбрасывал исключение именно этого типа и соответственно в основной программе обрабатывать это исключение:
Однако необязательно наследовать свой класс исключений именно от типа Exception, можно взять какой-нибудь другой производный тип. Например, в данном случае мы можем взять тип ArgumentException, который представляет исключение, генерируемое в результате передачи аргументу метода некорректного значения:
Каждый тип исключений может определять какие-то свои свойства. Например, в данном случае мы можем определить в классе свойство для хранения устанавливаемого значения:
В конструкторе класса мы устанавливаем это свойство и при обработке исключения мы его можем получить:
Как правильно создавать пользовательские исключения в C#
Данная статья предназначена прежде всего для новичков в мире .NET, но может быть полезна также и разработчикам с опытом, которые не до конца разобрались, как правильно строить свои user-defined exceptions с помощью C#.
Пример кода для данной статьи можно скачать здесь.
Создание простого исключения
Создавать собственные типы исключений в C# рекомендуется в тех случаях, когда нужно четко отделить возникшую в написанном программистом коде исключительную ситуацию, от исключения, возникающего в стандартных типах .NET Framework.
К примеру, есть метод, призванный изменять имя пользователя:
Данный метод будет решать возложенные на него задачи, но не будет делать одну важную вещь – он не сообщит о том, что пользователь с заданным именем отсутствует, и операция смены его имени не была выполнена.
Чтобы проинформировать о возникшей исключительной ситуации можно генерировать стандартное исключение, что не является рекомендуемой практикой:
Для того чтобы можно было легко определить, что исключение генерируется на уровне конкретного приложения, нужно создать свой – пользовательский Exception, и при получении null вместо нужного пользователя выбрасывать именно его.
Создать свой Exception не сложно – нужно определить public-класс, который будет наследоваться от System.Exception или System.ApplicationException. Хотя это и не является хорошей практикой, кода внутри созданного класса исключения можно не писать вообще:
Каждый из этих типов предназначен для конкретной цели. Тогда как System.Exception является общим классом для всех user-defined exceptions, то System.ApplicationException определяет исключения, возникающие на уровне конкретного приложения.
К примеру, тестовое приложения из данной статьи является отдельной программой, поэтому вполне допустимо наследовать определенный нами exception от System.ApplicationException.
Теперь вместо Exception мы сгенерируем созданный нами UserNotFoundException:
В таком случае в качестве сообщения о возникшем исключении будет: «Error in the application.». Что не очень информативно.
- класс исключения должен наследоваться от Exception/ApplicationException;
- класс должен быть помечен атрибутом [System.Serializable];
- класс должен определять стандартный конструктор;
- класс должен определять конструктор, который устанавливает значение унаследованного свойства Message;
- класс должен определять конструктор для обработки “внутренних исключений”;
- класс должен определять конструктор для поддержки сериализации типа.
Немного о предназначении отдельных конструкторов: конструктор для обработки “внутренних исключений” нужен для того, чтобы передать в него exception, послуживший причиной возникновения данного исключения. Подробнее, зачем нужен конcтруктор для поддержки сериализации типа под спойлером «Добавление дополнительных полей, их сериализация и десериализация» ниже.
Дабы избавить программиста от необходимости писать одинаковый код в Visual Studio есть сниппет «Exception», который генерирует класс исключения, соответствующий всем рекомендациям, перечисленным выше.
Итак, после воплощения рекомендаций в жизнь, код нашего исключения должен выглядеть примерно так:
Теперь при генерации исключения мы можем указать причину его возникновения более подробно:
Допустим, мы хотим добавить в класс нашего исключения дополнительное поле, хранящее имя пользователя, которого хотелось найти, но в итоге он не был найден (из-за чего собственно и было сгенерировано исключение). Добавляем к классу исключения дополнительное string-поле:
Проблема в том, что данные из добавленного нами поля не будут сериализоваться и десериализоваться автоматически. Мы должны позаботиться о том, чтобы CLR сериализовала и десериализовала данные по нашему исключению корректно.
Для сериализации поля мы должны переопределить метод GetObjectData, описываемый интерфейсом ISerializable. Метод GetObjectData заполняет объект SerializationInfo данными для сериализации. Именно в SerializationInfo мы должны передать имя нашего поля и информацию, хранящуюся в нем:
Метод GetObjectData для базового класса нужно вызвать для того, чтобы добавить в SerializationInfo все поля нашего исключения по умолчанию (такие как Message, TargetSite, HelpLink и т.д.).
Десериализация проходит в схожем ключе. При десериализации будет вызван конструктор нашего исключения, принимающий SerializationInfo и StreamingContext. Наша задача – получить из SerializationInfo данные и записать их в созданное нами поле:
И последний штрих – добавление в XML-документацию (если вы, конечно, ее используете) нашего метода информации о том, что он может выбросить исключение определенного типа:
Итак, наш user-defined exception готов к применению. Вы можете добавить к нему все что душе угодно: дополнительные поля, описывающие состояние исключения, содержащие дополнительную информацию и т.д.
In C# how do I define my own Exceptions?
provide the common constructors that are used by exceptions:
So, ideally, your custom Exception should look at least like this:
About the fact whether you should inherit from Exception or ApplicationException : FxCop has a rule which says you should avoid inheriting from ApplicationException :
CA1058 : Microsoft.Design :
Change the base type of ‘MyException’ so that it no longer extends ‘ApplicationException’. This base exception type does not provide any additional value for framework classes. Extend ‘System.Exception’ or an existing unsealed exception type instead. Do not create a new exception base type unless there is specific value in enabling the creation of a catch handler for an entire class of exceptions.
See the page on MSDN regarding this rule.
![]()
It seems that I’ve started a bit of an Exception sublcassing battle. Depending on the Microsoft Best Practices guide you follow. you can either inherit from System.Exception or System.ApplicationException. There’s a good (but old) blog post that tries to clear up the confusion. I’ll keep my example with Exception for now, but you can read the post and chose based on what you need:
There is a battle no more! Thanks to Frederik for pointing out FxCop rule CA1058 which states that your Exceptions should inherit from System.Exception rather than System.ApplicationException:
Define a new class that inherits from Exception (I’ve included some Constructors. but you don’t have to have them):
And elsewhere in your code to throw:
EDIT
Updated with changes to ensure a Serializable Exception. Details can be found here:
Pay close attention to the section about steps that need to be taken if you add custom Properties to your Exception class.
Thanks to Igor for calling me on it!
To define your own exception class:
Define a class that inherits from Exception. If necessary, define any unique members needed by your class to provide additional information about the exception. For example, the ArgumentException class includes a ParamName property that specifies the name of the parameter whose argument caused the exception, and the RegexMatchTimeoutException property includes a MatchTimeout property that indicates the time-out interval.
If necessary, override any inherited members whose functionality you want to change or modify. Note that most existing derived classes of Exception do not override the behavior of inherited members.
Determine whether your custom exception object is serializable. Serialization enables you to save information about the exception and permits exception information to be shared by a server and a client proxy in a remoting context. To make the exception object serializable, mark it with the SerializableAttribute attribute.
Define the constructors of your exception class. Typically, exception classes have one or more of the following constructors:
Exception(), which uses default values to initialize the properties of a new exception object.
Exception(String), which initializes a new exception object with a specified error message.
Exception(String, Exception), which initializes a new exception object with a specified error message and inner exception.
Exception(SerializationInfo, StreamingContext), which is a protected constructor that initializes a new exception object from serialized data. You should implement this constructor if you’ve chosen to make your exception object serializable.
Урок 33. Создание исключений в C#
Тридцать третий урок учебника по основам C# завершает исследование обработки исключений. В этой статье мы рассмотрим создание (выбрасывание) исключений для сообщения об ошибках. Это включает использование стандартных и пользовательских типов исключений.
Зачем создавать исключения?
В предыдущей части учебника по основам C# я представил обработку исключений и средство для перехвата исключений с помощью блока try / catch / finally в C#. В более старых языках вы можете выйти из подпрограммы рано и предоставить код возврата для указания состояния. Это возможно с помощью C#, но не использует возможности обработки исключений, предоставляемые языком. Предпочтительно явно вызывать или выбрасывать исключения при возникновении условий ошибки и разрешать захват исключений блоком try / catch / finally или системой выполнения C#.
Исключения следует создавать только при возникновении непредвиденного или недопустимого действия, которое не позволяет методу выполнить свою обычную функцию; обработка исключений не должна использоваться для нормального потока программы вместо условной обработки. Может быть трудно поддерживать код, который неправильно использует обработку исключений.
Хорошая практика создания исключений включает в себя:
- создание исключения при передаче недопустимого значения параметра в метод. В этом случае необходимо создать исключение ArgumentException или один из его производных классов исключений.
- создание исключения при вызове метода, который не может работать, потому что другие шаги должны быть предприняты заранее. В этом случае может быть вызвано исключение InvalidOperationException.
Чтобы вызвать исключение, вы используете команду throw. Синтаксис выглядит следующим образом:
Исключение — это объект исключения, содержащий сведения о возникшей ошибке. Он может быть объявлено как объект и инициализирован перед командой throw или включен в инструкцию throw, используя ключевое слово new и один из конструкторов исключения для задания свойств объекта.
В следующем примере используется консольное приложение, для работы которого требуется хотя бы один параметр запуска. Если параметр запуска не указан, создается исключение ArgumentException. Конструктор имеет один строковый параметр, содержащий сообщение об ошибке.
При запуске с параметром программа запускается без ошибок и выводит количество предоставленных параметров. Если аргументы не указаны, тело инструкции if создает исключение. Это позволяет избежать непредвиденных исключений, возникающих позже из-за недопустимых параметров.
Примечание: класс объекта исключения, который нужно бросить, должен соответствовать условию ошибки. Каждый объект исключения имеет конструкторы, которые могут отличаться от показанных выше. Дополнительные сведения о других стандартных классах исключений смотри на сайте MSDN.
Повторное выбрасывание исключений
При перехвате исключения в блоке catch исключение считается обработанным, и выполнение кода продолжается в обычном режиме. Иногда вы захотите поймать исключение, но все равно его бросят, чтобы снова поймать. Например, можно перехватывать и регистрировать исключения, а затем повторно выбрасывать их для обработки вызывающей функцией.
Когда исключение создается явно с использованием предыдущего синтаксиса, создается новый объект исключения. Это содержит только основную информацию, которую вы указываете; информация, такая как трассировка стека исходного исключения, теряется. Чтобы повторно создать исходное исключение и сохранить все дополнительные сведения, используйте команду throw без указания объекта исключения:
В следующем примере показано, как используется этот синтаксис. Для простоты вызываемые методы не определены, поэтому этот код не может быть выполнен напрямую.
Свойство InnerException
В предыдущей статье учебника я описал свойство InnerException, которое может использоваться для хранения сведений о начальном исключении, вызвавшем дальнейшее исключение. Этот механизм полезен, когда вы хотите бросить одно исключение в ответ на другое, не теряя детали исходной проблемы.
В следующем примере расширяется пример re-throw выше путем создания FileNotFoundException с новым сообщением об ошибке и сведения о перехваченном исключении в свойстве InnerException.
Примечание: код выше приведен в качестве примера установки свойства InnerException. Это не очень хорошая практика, чтобы использовать catch-all и бросить новое исключение, так как это может замаскировать серьезные проблемы.
Пользовательские исключения
Платформа .NET Framework предоставляет богатый набор типов исключений, которые можно создавать и перехватывать. Однако список доступных исключений не охватывает все возможные варианты. Часто бывает целесообразно определить пользовательские исключения для конкретных сценариев. Это может быть достигнуто путем получения нового класса исключений из любого из существующих типов, обычно класса ApplicationException.
В заключительном разделе этой статьи мы определим новый класс исключений и продемонстрируем, как он может быть брошен и обработан. Это будет простое исключение без пользовательских методов или свойств. Они могут быть добавлены в производный класс с помощью стандартных методов объектно-ориентированного программирования.
Определение пользовательского исключения
Наше пользовательское исключение будет сообщать об исключениях при печати страницы с полями, которые недопустимы для выбранного принтера. В соответствии с соглашениями об именах новый класс будет называться InvalidPrinterMarginsException. Чтобы определить новый класс в текущем пространстве имен и указать, что он является производным от ApplicationException, используйте следующий код:
Класс ApplicationException предоставляет три основных конструктора. Первый не требует параметров и создает исключение со значениями свойств по умолчанию. Второй принимает строковый параметр, содержащий сообщение об ошибке. Конечный конструктор позволяет указать сообщение и внутренний объект исключения. Эти три конструктора не наследуются автоматически новым типом исключения, поэтому их следует добавить следующим образом:
Созданиие перехват пользовательского исключения
Как только пользовательское исключение создано, оно может быть вызвано и поймано таким же образом, как и любое другое. Это включает в себя перехват точного исключения или нескольких типов, из которых оно получено. Действительно, одно пользовательское исключение может наследоваться от другого, чтобы создать глубокую иерархию исключений приложения для гибкой обработки ошибок.
В последнем примере пользовательское исключение сочетается с кодом консольного приложения. Эта программа создает пользовательское исключение в блоке try. Сообщение об исключении выводится следующим блоком catch.
Заключительное замечание по ApplicationException
Когда Microsoft создала платформу .NET framework, они определили два базовых класса. Это были системные исключения (SystemException) и пользовательские исключения (ApplicationException). При рассмотрении этой сегрегации Microsoft поняла, что преимущества такой структуры не были ожидаемыми. По этой причине Корпорация Майкрософт теперь рекомендует создавать пользовательские исключения непосредственно из класса Exception. Это делает иерархию исключений более плоской. Исключение ApplicationException остается частью платформы .NET framework для обеспечения обратной совместимости на момент написания статьи.
Автор этого материала — я — Пахолков Юрий. Я оказываю услуги по написанию программ на языках Java, C++, C# (а также консультирую по ним) и созданию сайтов. Работаю с сайтами на CMS OpenCart, WordPress, ModX и самописными. Кроме этого, работаю напрямую с JavaScript, PHP, CSS, HTML — то есть могу доработать ваш сайт или помочь с веб-программированием. Пишите сюда.
статьи IT, уроки по си шарп, си шарп, исключения, ошибки