Выбрать главу

Кроме того, в классе Exception определяется ряд методов. Чаще всего приходится пользоваться методом ToString(), возвращающим символьную строку с описанием исключения. Этот метод автоматически вызывается, например, при отображении ис ключения с помощью метода WriteLine().

Применение всех трех упомянутых выше свойств и метода из класса Exception демонстрируется в приведенном ниже примере программы. // Использовать члены класса Exception. using System; class ExcTest { public static void GenException() { int[] nums = new int[4]; Console.WriteLine("До генерирования исключения."); // Сгенерировать исключение в связи с выходом за границы массива. for(int i=0; i < 10; i++) { nums[i] = i; Console.WriteLine("nums[{0}]: (1)", i, nums[i]); } Console.WriteLine("He подлежит выводу"); } } class UseExcept { static void Main() { try { ExcTest.GenException(); } catch (IndexOutOfRangeException exc) { Console.WriteLine("Стандартное сообщение таково: "); Console.WriteLine(exc); // вызвать метод ToString() Console.WriteLine("Свойство StackTrace: " + exc.StackTrace); Console.WriteLine("Свойство Message: " + exc.Message); Console.WriteLine("Свойство TargetSite: " + exc.TargetSite); } Console.WriteLine("После блока перехвата исключения."); } }

При выполнении этой программы получается следующий результат. До генерирования исключения. nums[0]: 0 nums[1]: 1 nums[2]: 2 nums[3]: 3 Стандартное сообщение таково: System.IndexOutOfRangeException: Индекс находился вне границ массива. в ExcTest.genException() в <имя_файла>:строка 15 в UseExcept.Main() в <имя_файла>:строка 29 Свойство StackTrace:в ExcTest.genException() в <имя_файла>:строка 15 в UseExcept.Main()в <имя_файла>:строка 29 Свойство Message: Индекс находился вне границ массива. Свойство TargetSite: Void genException() После блока перехвата исключения.

В классе Exception определяются четыре следующих конструктора. public Exception() public Exception(string сообщение) public Exception(string сообщение, Exception внутреннее_исключение) protected Exception(System.Runtime.Serialization.SerializationInfo информация, System.Runtime.Serialization.StreamingContext контекст)

Первый конструктор используется по умолчанию. Во втором конструкторе ука зывается строка сообщение, связанная со свойством Message, которое имеет отно шение к генерируемому исключению. В третьем конструкторе указывается так на зываемое внутреннее исключение. Этот конструктор используется в том случае, когда одно исключение порождает другое, причем внутреннее_исключение обозначает первое исключение, которое будет пустым, если внутреннее исключение отсутствует. (Если внутреннее исключение присутствует, то оно может быть получено из свойства InnerException, определяемого в классе Exception.) И последний конструктор об рабатывает исключения, происходящие дистанционно, и поэтому требует десериали зации.

Следует также заметить, что в четвертом конструкторе класса Exception типы SerializationInfo и StreamingContext относятся к пространству имен System. Runtime.Serialization. Наиболее часто используемые исключения

В пространстве имен System определено несколько стандартных, встроенных ис ключений. Все эти исключения являются производными от класса SystemException, поскольку они генерируются системой CLR при появлении ошибки во время выпол нения. В табл. 13.1 перечислены некоторые наиболее часто используемые стандартные исключения.

Таблица 13.1. Наиболее часто используемые исключения, определенные в пространстве имен System Исключение Значение ArrayTypeMismatchException Тип сохраняемого значения несовместим с типом массива DivideByZeroException Попытка деления на нуль IndexOutOfRangeException Индекс оказался за границами массива InvalidCastException Неверно выполнено динамическое приведение типов OutOfMemoryException Недостаточно свободной памяти для дальнейшего выполнения программы. Это исключение может быть, например, сгенерировано, если для создания объекта с помощью оператора new не хватает памяти OverflowException Произошло арифметическое переполнение NullReferenceException Попытка использовать пустую ссылку, т.е. ссылку, которая не указывает ни на один из объектов

Большинство исключений, приведенных в табл. 13.1, не требует особых пояснений, кроме исключения NullReferenceException. Это исключение генерируется при по пытке использовать пустую ссылку на несуществующий объект, например, при вы зове метода по пустой ссылке. Пустой называется такая ссылка, которая не указывает ни на один из объектов. Для того чтобы создать такую ссылку, достаточно, например, присвоить явным образом пустое значение переменной ссылочного типа, используя ключевое слово null. Пустые ссылки могут также появляться и другими, менее оче видными путями. Ниже приведен пример программы, демонстрирующий обработку исключения NullReferenceException. // Продемонстрировать обработку исключения NullReferenceException. using System; class X { int x; public X(int a) { x = a; } public int Add(X o) { return x + o.x; } } // Продемонстрировать генерирование и обработку // исключения NullReferenceException. class NREDemo { static void Main() { X p = new X(10); X q = null; // присвоить явным образом пустое значение переменной q int val; try { val = p.Add(q); // эта операция приведет к исключительной ситуации } catch (NullReferenceException) { Console.WriteLine("Исключение NullReferenceException!"); Console.WriteLine("Исправление ошибки...\n"); // А теперь исправить ошибку. q = new X(9); val = p.Add(q); } Console.WriteLine("Значение val равно {0}", val); } }

Вот к какому результату приводит выполнение этой программы. Исключение NullReferenceException! Исправление ошибки... Значение val равно 19

В приведенном выше примере программы создается класс X, в котором определя ются член х и метод Add(), складывающий значение члена х в вызывающем объекте со значением члена х в объекте, передаваемом этому методу в качестве параметра. Оба объекта класса X создаются в методе Main(). Первый из них (переменная р) инициа лизируется, а второй (переменная q) — нет. Вместо этого переменной q присваивается пустое значение. Затем вызывается метод р.Add() с переменной q в качестве аргумен та. Но поскольку переменная q не ссылается ни на один из объектов, то при попытке получить значение члена q.х генерируется исключение NullReferenceException. Получение производных классов исключений

Несмотря на то что встроенные исключения охватывают наиболее распространен ные программные ошибки, обработка исключительных ситуаций в 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.SerializationInfo si, System.Runtime.Serialization.StreamingContext sc) : base(si, sc) { } // Переопределить метод ToString() для класса исключения RangeArrayException. public override string ToString() { return Message; }