Создание пользовательских классов исключений

У System.Exception имеются три общедоступных конструктора и один защищенный. Первый — это конструктор по умолчанию, который на самом деле мало что делает.

Второй — конструктор, принимающий ссылку на строковый объект. Строка представляет собой общее, определяемое программистом сообщение, которое можно рассматривать как более дружественное к пользователю описание исключения. Третий конструктор также принимает строку сообщения, как и второй, но вдобавок принимает ссылку на другой объект Exception.

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

В этом случае исполняющая система генерирует исключение TypelnitializationException, но только после установки внутреннего исключения в исходное исключение, чтобы тот, кто перехватывает TypelnitializationException, по крайней мере, знал, чем вызвано исключение изначально. И, наконец, защищенный конструктор позволяет создавать исключение из объекта Serializationlnfo.

Сериализуемые исключения создаются, чтобы их можно было использовать через границы контекстов, например, с .NET Remoting. Это значит, что пользовательские классы исключений также понадобится пометить атрибутом SerializableAttribute.

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

Таким образом, тип исключения будет более выразительным в описании вызвавшей его проблемы. Еще лучше то, что производный класс может содержать данные, которые соответствуют причине генерации данного исключения. И помните, что в С# все исключения должны наследоваться от System.Exception.

Давайте посмотрим, что нужно сделать для эффективного определения пользовательских исключений.

Возьмем предыдущий пример EmployeeDatabase. Предположим, что перед добавлением сотрудника в базу данных его данные нужно проверить на предмет достоверности. Если данные сотрудника по каким-то причинам не подойдут, метод Add сгенерирует исключение типа EmployeeVerificationException.

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

Посмотрим, как может выглядеть такое исключение:

using System;
using System.Runtime.Serialization;
[Serializable ()]
public class EmployeeVerificationException : Exception {
public enum Cause { InvalidSSN, InvalidBirthDate
}
public EmployeeVerificationException( Cause reason ) :base()  {
this.Reason = reason;
}
public EmployeeVerificationException( Cause reason.String msg ) :base ( msg )  {
this.Reason = reason;
}
public EmployeeVerificationException ( Cause reason.String msg. Exception inner ) :base( msg, inner )  {
this.Reason = reason;
}
protected EmployeeVerificationException(
Serializationlnfо info, StreamingContext context ) :base( info, context )  {
}
public Cause Reason { get; private set; }
}

В методе EmployeeDatabase.Add показан пример вызова Validate на объекте emp. Это черновой пример, где проверка должна завершиться неудачей, сгенерировав исключение EmployeeVerif icationException.

Главная цель этого примера — создание нового типа исключения. В будущем еще не раз придется убедиться в том, что простого создания нового типа исключения вполне достаточно для доставки дополнительной информации.

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

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

Эта цель частично была утеряна из-за того, что некоторые исключения, определенные в .NET Framework, наследуются от ApplicationException.

Определение целых четырех конструкторов для такого простого типа исключений может удивить. Дело в том, что при определении новых типов исключений действует традиционная идиома, требующая определения тех же самых четырех общедоступных конструкторов, которые есть в System.Exception.

Если бы не планировалось передавать дополнительные данные о причине исключения, то конструкторы EmployeeVerif icationException в точности соответствовали по форме конструкторам System.Exception.

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

Вдобавок унаследованное исключение сможет использовать сообщение и внутреннее исключение, которые уже инкапсулированы в System.Exception.

Вы можете следить за любыми ответами на эту запись через RSS 2.0 ленту. Вы можете оставить ответ, или trackback с вашего собственного сайта.

Оставьте отзыв

XHTML: Вы можете использовать следующие теги: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

 
Rambler's Top100