C# 4.0 полное руководство - 2011 - Герберт Шилдт
Шрифт:
Интервал:
Закладка:
Получение производных классов исключений
Несмотря на то что встроенные исключения охватывают наиболее распространенные программные ошибки, обработка исключительных ситуаций в C# не ограничивается только этими ошибками. В действительности одна из сильных сторон принятого в C# подхода к обработке исключительных ситуаций состоит в том, что в этом языке допускается использовать исключения, определяемые пользователем, т.е. тем, кто программирует на С#. В частности, такие специальные исключения можно использовать для обработки ошибок в собственном коде, а создаются они очень просто. Для этого достаточно определить класс, производный от класса Exception. В таких классах совсем не обязательно что-то реализовывать — одного только их существования в системе типов уже достаточно, чтобы использовать их в качестве исключений.
ПРИМЕЧАНИЕ
В прошлом специальные исключения создавались как производные от класса Application.Exception, поскольку эта иерархия классов была первоначально зарезервирована для исключений прикладного характера. Но теперь корпорация Microsoft не рекомендует этого делать, а вместо этого получать исключения, производные от класса Exception. Именно по этой причине данный подход и рассматривается в настоящей книге.
Создаваемые пользователем классы будут автоматически получать свойства и методы, определенные в классе Exception и доступные для них. Разумеется, любой из этих членов класса Exception можно переопределить в создаваемых классах исключений.
Когда создается собственный класс исключений, то, как правило, желательно, чтобы в нем поддерживались все конструкторы, определенные в классе Exception. В простых специальных классах исключений этого нетрудно добиться, поскольку для этого достаточно передать подходящие аргументы соответствующему конструктору класса Exception, используя ключевое слово base. Но формально нужно предоставить только те конструкторы, которые фактически используются в программе.
Рассмотрим пример программы, в которой используется исключение специального типа. Напомним, что в конце главы 10 был разработан класс RangeArray, поддерживающий одномерные массивы, в которых начальный и конечный индексы определяются пользователем. Так, например, вполне допустимым считается массив, индексируемый в пределах от -5 до 27. Если же индекс выходил за границы массива, то для обработки этой ошибки в классе RangeArray была определена специальная переменная. Такая переменная устанавливалась и проверялась после каждой операции обращения к массиву в коде, использовавшем класс RangeArray. Безусловно, такой подход к обработке ошибок "неуклюж" и чреват дополнительными ошибками. В приведенном ниже улучшенном варианте класса RangeArray обработка ошибок нарушения границ
массива выполняется более изящным и надежным способом с помощью специально генерируемого исключения.
// Использовать специальное исключение для обработки // ошибок при-обращении к массиву класса RangeArray.
using System;
// Создать исключение для класса RangeArray. class RangeArrayException : Exception {
/* Реализовать все конструкторы класса Exception. Такие конструкторы просто реализуют конструктор базового класса. А поскольку класс исключения RangeArrayException ничего не добавляет к классу Exception, то никаких дополнительных действий не требуется. */ public RangeArrayException() : base() { }
public RangeArrayException(string str) : base(str) { }
public RangeArrayException(
string str, Exception inner) : base(str, inner) { } protected RangeArrayException(
System.Runtime.Serialization.SerializationInfо si,
System.Runtime.Serialization.StreamingContext sc) : base(si, sc) { }
// Переопределить метод ToStringO для класса исключения RangeArrayException. public override string ToStringO { return Message;
}
}
// Улучшенный вариант класса RangeArray. class RangeArray {
// Закрытые данные.
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("Нижний индекс не меньше верхнего.");
}
а = 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);
i
// Использовать объект га в качестве массива.
Console.WriteLine("Длина массива га: и- + га.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("Длина массива га2: " + 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 гаЗ = new RangeArray(100, -10); // Ошибка!
} catch (RangeArrayException exc) {
Console.WriteLine(exc);
}
// Использовать неверно заданный индекс, try {
RangeArray гаЗ = new RangeArray(-2, 2);
for(int i = -2; i <= 2; i++) ra3[i] = i;
Console.Write("Содержимое массива гаЗ: ");
for (int i = -2; i <= 10; i++) // сгенерировать ошибку нарушения границ Console.Write(гаЗ[i] + " ");
} catch (RangeArrayException exc) {
Console.WriteLine(exc);
}
}
}
После выполнения этой программы получается следующий результат.
Длина массива га: 11
Содержимое массива га: -5-4-3-2-1012345 Длина массива га2: 10
Содержимое массива га2: 12345678910
Сгенерировать ошибки нарушения границ.
Нижний индекс не меньше верхнего.
Содержимое массива гаЗ: -2-1012 Ошибка нарушения границ.
Когда возникает ошибка нарушения границ массива класса RangeArray, генерируется объект типа RangeArrayException. В классе RangeArray это может произойти в трех следующих местах: в аксессоре get индексатора, в аксессоре set индексатора и в конструкторе класса RangeArray. Для перехвата этих исключений подразумевается, что объекты типа RangeArray должны быть сконструированы и доступны из блока try, что и продемонстрировано в приведенной выше программе. Используя специальное исключение для сообщения об ошибках, класс RangeArray теперь действует как один из встроенных в C# типов данных, и поэтому он может быть полностью интегрирован в механизм обработки ошибок, обнаруживаемых в программе.
Обратите внимание на то, что в теле конструкторов класса исключения RangeArrayException отсутствуют какие-либо операторы, но вместо этого они просто передают свои аргументы классу Exception, используя ключевое слово base. Как пояснялось ранее, в тех случаях, когда производный класс исключений не дополняет функции базового класса, весь процесс создания исключений можно поручить конструкторам класса Exception. Ведь производный класс исключений совсем не обязательно должен чем-то дополнять функции, наследуемые от класса Exception.
Прежде чем переходить к дальнейшему чтению, попробуйте немного поэкспериментировать с приведенной выше программой. В частности, попробуйте закомментировать переопределение метода ToString () и понаблюдайте за результатами. Кроме того, попытайтесь создать исключение, используя конструктор, вызываемый по умолчанию, и посмотрите, какое сообщение при этом сформируется стандартными средствами С#.