Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Обработка исключительных ситуаций.docx
Скачиваний:
0
Добавлен:
01.07.2025
Размер:
91 Кб
Скачать
☆

13.11 Получение производных классов исключений

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

В прошлом специальные исключения создавались как производные от класса Application.Exception, поскольку эта иерархия классов была первоначально зарезер­вирована для исключений прикладного характера. Но теперь корпорация Microsoft не реко­мендует этого делать, а вместо этого получать исключения, производные от класса Exception.

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

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

Рассмотрим пример программы, в которой используется исключение специального типа. Напомним, что в конце главы 10 был разработан класс RangeArray, поддержи­вающий одномерные массивы, в которых начальный и конечный индексы определяют­ся пользователем. Так, например, вполне допустимым считается массив, индексируе­мый в пределах от -5 до 27. Если же индекс выходил за границы массива, то для обра­ботки этой ошибки в классе RangeArray была определена специальная переменная. Такая переменная устанавливалась и проверялась после каждой операции обращения к массиву в коде, использовавшем класс RangeArray. Безусловно, такой подход к об­работке ошибок "неуклюж" и чреват дополнительными ошибками. В приведенном ниже улучшенном варианте класса RangeArray обработка ошибок нарушения границ массива выполняется более изящным и надежным способом с помощью специально генерируемого исключения.

Листинг 13.14

// Использовать специальное исключение для обработки

// ошибок при обращении к массиву класса RangeArray.

using System;

// Создать исключение для класса RangeArray.

class RangeArrayException : Exception

{

/* Реализовать все конструкторы класса Exception. Такие

Конструкторы просто реализуют конструктор базового класса.

А поскольку класс исключения RangeArrayException ничего

не добавляет к классу Exception, то никаких дополнительных

действий не требуется */

public RangeArrayException() : base() { }

public RangeArrayException(string message) : base(message) { }

public RangeArrayException(string message,

Exception innerException) :

base(message, innerException) { }

protected RangeArrayException

(

System.Runtime.Serialization.SerializationInfo info,

System.Runtime.Serialization.StreamingContext context):

base(info, context) { }

// Переопределить метод ToString()

// для класса исключения RangeArrayException.

public override string ToString()

{

return Message;

}

}

// Улучшенный вариант класса RangeArray.

class RangeArray {

// Private data.

int[] a; // ссылка на базовый массив

int lowerBound; // наименьший индекс

int upperBound; // наибольший индекс

// Автоматически реализуемое и доступное только для чтения

// свойство Length.

public int Length { get; private set; }

// Построить массив по заданному размеру.

public RangeArray(int low, int high)

{

high++;

if(high <= low) {

throw new RangeArrayException("Нижний индекс не меньше верхнего.");

}

a = new int[high - low];

Length = high - low;

lowerBound = low;

upperBound = --high;

}

// Этот индексатор для класса RangeArray.

public int this[int index] {

// Это аксессор get.

get {

if(ok(index)) {

return a[index - lowerBound];

} else {

throw new RangeArrayException("Ошибка нарушения границ.");

}

}

// Это аксессор set.

set {

if(ok(index)) {

a[index - lowerBound] = value;

}

else throw new RangeArrayException("Ошибка нарушения границ.");

}

}

// Возвратить логическое значение true, если

// индекс находится в установленных границах.

private bool ok(int index)

{

if(index >= lowerBound & index <= upperBound) return true;

return false;

}

}

// Продемонстрировать применение массива с произвольно

// задаваемыми пределами индексирования.

class RangeArrayDemo

{

static void Main()

{

try {

RangeArray ra = new RangeArray(-5, 5);

RangeArray ra2 = new RangeArray(1, 10);

// Использовать объект ra в качестве массива.

Console.WriteLine("Длина массива ra: " + ra.Length);

for(int i = -5; i <= 5; i++)

ra[i] = i;

Console.Write("Содержимое массива ra: ");

for(int i = -5; i <= 5; i++)

Console.Write(ra[i] + " ");

Console.WriteLine("\n");

// Использовать объект ra2 в качестве массива.

Console.WriteLine("Длина массива ra2: " + ra2.Length);

for(int i = 1; i <= 10; i++)

ra2[i] = i;

Console.Write("Содержимое массива ra2: ");

for(int i = 1; i <= 10; i++)

Console.Write(ra2[i] + " ");

Console.WriteLine("\n");

} catch (RangeArrayException exc) {

Console.WriteLine(exc);

}

// А теперь продемонстрировать обработку некоторых ошибок.

Console.WriteLine("Сгенерировать ошибки нарушения границ.");

// Использовать неверно заданный индекс.

try {

RangeArray ra3 = new RangeArray(100, -10); // Error

} catch (RangeArrayException exc) {

Console.WriteLine(exc);

}

// Use an invalid index.

try

{

RangeArray ra3 = new RangeArray(-2, 2);

for(int i = -2; i <= 2; i++)

ra3[i] = i;

Console.Write("Содержимое массива ra3: ");

for(int i = -2; i <= 10; i++) // Сгенерировать ошибку

// нарушения границ

Console.Write(ra3[i] + " ");

} catch (RangeArrayException exc) {

Console.WriteLine(exc);

}

}

}

После выполнения этой программы получается следующий результат.

Длина массива rа: 11

Содержимое массива rа: -5-4-3-2-1 0 1 2 3 4 5

Длина массива rа2: 10

Содержимое массива ra2: 1 2 3 4 5 6 7 8 9 10

Сгенерировать ошибки нарушения границ.

Нижний индекс не меньше верхнего.

Содержимое массива rа3: -2-1012 Ошибка нарушения границ.

Когда возникает ошибка нарушения границ массива класса RangeArray, генери­руется объект типа RangeArrayException. В классе RangeArray это может произой­ти в трех следующих местах: в аксессоре get индексатора, в аксессоре set индексатора и в конструкторе класса RangeArray. Для перехвата этих исключений подразумева­ется, что объекты типа RangeArray должны быть сконструированы и доступны из блока try, что и продемонстрировано в приведенной выше программе. Используя специальное исключение для сообщения об ошибках, класс RangeArray теперь дей­ствует как один из встроенных в С# типов данных, и поэтому он может быть полностью интегрирован в механизм обработки ошибок, обнаруживаемых в программе.

Обратите внимание на то, что в теле конструкторов класса исключения RangeArrayException отсутствуют какие-либо операторы, но вместо этого они про­сто передают свои аргументы классу Exception, используя ключевое слово base. Как пояснялось ранее, в тех случаях, когда производный класс исключений не дополняет функции базового класса, весь процесс создания исключений можно поручить кон­структорам класса Exception. Ведь производный класс исключений совсем не обяза­тельно должен чем-то дополнять функции, наследуемые от класса Exception.

Прежде чем переходить к дальнейшему чтению, попробуйте немного поэксперимен­тировать с приведенной выше программой. В частности, попробуйте закомментировать переопределение метода ToString() и понаблюдайте за результатами. Кроме того, по­пытайтесь создать исключение, используя конструктор, вызываемый по умолчанию, и посмотрите, какое сообщение при этом сформируется стандартными средствами С#.