}После выполнения этой программы получается следующий результат.
Длина массива ra: 11Содержимое массива ra: -5 -4 -3 -2 -1 0 1 2 3 4 5
Длина массива ra2: 10Содержимое массива ra2: 1 2 3 4 5 6 7 8 9 10
Сгенерировать ошибки нарушения границ.Нижний индекс не меньше верхнего.Содержимое массива ra3: -2 -1 0 1 2 Ошибка нарушения границ.Когда возникает ошибка нарушения границ массива класса RangeArray, генерируется объект типа RangeArrayException. В классе RangeArray это может произойти в трех следующих местах: в аксессоре get индексатора, в аксессоре set индексатораи в конструкторе класса RangeArray. Для перехвата этих исключений подразумевается, что объекты типа RangeArray должны быть сконструированы и доступны изблока try, что и продемонстрировано в приведенной выше программе. Используяспециальное исключение для сообщения об ошибках, класс RangeArray теперь действует как один из встроенных в C# типов данных, и поэтому он может быть полностьюинтегрирован в механизм обработки ошибок, обнаруживаемых в программе.Обратите внимание на то, что в теле конструкторов класса исключенияRangeArrayException отсутствуют какие-либо операторы, но вместо этого они просто передают свои аргументы классу Exception, используя ключевое слово base. Какпояснялось ранее, в тех случаях, когда производный класс исключений не дополняетфункции базового класса, весь процесс создания исключений можно поручить конструкторам класса Exception. Ведь производный класс исключений совсем не обязательно должен чем-то дополнять функции, наследуемые от класса Exception.Прежде чем переходить к дальнейшему чтению, попробуйте немного поэкспериментировать с приведенной выше программой. В частности, попробуйте закомментироватьпереопределение метода ToString() и понаблюдайте за результатами. Кроме того, попытайтесь создать исключение, используя конструктор, вызываемый по умолчанию, ипосмотрите, какое сообщение при этом сформируется стандартными средствами С#.## Перехват исключений производных классовПри попытке перехватить типы исключений, относящихся как к базовым, таки к производным классам, следует особенно внимательно соблюдать порядок следования операторов catch, поскольку перехват исключения базового класса будет совпадать с перехватом исключений любых его производных классов. Например, классException является базовым для всех исключений, и поэтому вместе с исключениемтипа Exception могут быть перехвачены и все остальные исключения производных отнего классов. Конечно, для более четкого перехвата всех исключений можно воспользоваться упоминавшейся ранее формой оператора catch без указания конкретного типаисключения. Но вопрос перехвата исключений производных классов становится весьмаактуальным и в других ситуациях, особенно при создании собственных исключений.Если требуется перехватывать исключения базового и производного классов, топервым по порядку должен следовать оператор catch, перехватывающий исключениепроизводного класса. Это правило необходимо соблюдать потому, что при перехватеисключения базового класса будут также перехвачены исключения всех производныхот него классов. Правда, это правило соблюдается автоматически: если первым расположить в коде оператор catch, перехватывающий исключение базового класса, тово время компиляции этого кода будет выдано сообщение об ошибке.В приведенном ниже примере программы создаются два класса исключений:ExceptA и ExceptB. Класс ExceptA является производным от класса Exception,а класс ExceptB — производным от класса ExceptA. Затем в программе генерируютсяисключения каждого типа. Ради краткости в классах специальных исключений предоставляется только один конструктор, принимающий символьную строку, описывающую исключение. Но при разработке программ коммерческого назначения в классахспециальных исключений обычно требуется предоставлять все четыре конструктора,определяемых в классе Exception.
// Исключения производных классов должны появляться до// исключений базового класса.using System;
// Создать класс исключения.class ExceptA : Exception { public ExceptA(string str) : base(str) { }public override string ToString() { return Message;}
}
// Создать класс исключения, производный от класса ExceptA.class ExceptB : ExceptA { public ExceptB(string str) : base(str) { }public override string ToString() { return Message;}
}
class OrderMatters { static void Main() { for(int x = 0; x < 3; x++) { try { if(x==0) throw new ExceptA("Перехват исключения типа ExceptA"); else if(x==1) throw new ExceptB("Перехват исключения типа ExceptB"); else throw new Exception)); } catch (ExceptB exc) { Console.WriteLine(exc); } catch (ExceptA exc) { Console.WriteLine(exc); } catch (Exception exc) { Console.WriteLine(exc); } } }}Вот к какому результату приводит выполнение этой программы.
Перехват исключения типа ExceptA.Перехват исключения типа ExceptB.System.Exception: Выдано исключение типа "System.Exception". в OrderMatters.Main() в <имя_файла>:строка 36Обратите внимание на порядок следования операторов catch. Именно в таком порядке они и должны выполняться. Класс ExceptB является производным от классаExceptA, поэтому исключение типа ExceptB должно перехватываться до исключениятипа ExceptA. Аналогично, исключение типа Exception (т.е. базового класса для всехисключений) должно перехватываться последним. Для того чтобы убедиться в этом,измените порядок следования операторов catch. В итоге это приведет к ошибке вовремя компиляции.Полезным примером использования оператора catch, перехватывающего исключения базового класса, служит перехват всей категории исключений. Допустим, чтосоздается ряд исключений для управления некоторым устройством. Если сделать ихклассы производными от общего базового класса, то в тех приложениях, где необязательно выяснять конкретную причину возникшей ошибки, достаточно перехватыватьисключение базового класса и тем самым исключить ненужное дублирование кода.## Применение ключевых слов checked и uncheckedВ C# имеется специальное средство, связанное с генерированием исключений, возникающих при переполнении в арифметических вычислениях. Как вам должно бытьуже известно, результаты некоторых видов арифметических вычислений могут превышать диапазон представления чисел для типа данных, используемого в вычислении.В этом случае происходит так называемое переполнение. Рассмотрим в качестве примера следующий фрагмент кода.
byte a, b, result;а = 127;b = 127;result = (byte)(а * b);В этом коде произведение значений переменных а и b превышает диапазон представления чисел для типа byte. Следовательно, результат вычисления данного выражения приводит к переполнению для типа данных, сохраняемого в переменнойresult.В C# допускается указывать, будет ли в коде сгенерировано исключение при переполнении, с помощью ключевых слов checked и unchecked. Так, если требуется указать, что выражение будет проверяться на переполнение, следует использовать ключевое слово checked, а если требуется проигнорировать переполнение — ключевоеслово unchecked. В последнем случае результат усекается, чтобы не выйти за пределыдиапазона представления чисел для целевого типа выражения.У ключевого слова checked имеются две общие формы. В одной форме проверяется конкретное выражение, и поэтому она называется операторной. А в другой формепроверяется блок операторов, и поэтому она называется блочной. Ниже приведены обеформы:
checked (выражение)
checked { // проверяемые операторы}где выражение обозначает проверяемое выражение. Если вычисление проверяемого выражения приводит к переполнению, то генерируется исключениеOverflowException.У ключевого слова unchecked также имеются две общие формы. В первой, операторной форме переполнение игнорируется при вычислении конкретного выражения.А во второй, блочной форме оно игнорируется при выполнении блока операторов:
unchecked (выражение)
unchecked { // операторы, для которых переполнение игнорируется}где выражение обозначает конкретное выражение, при вычислении которого переполнение игнорируется. Если же в непроверяемом выражении происходит переполнение,то результат его вычисления усекается.Ниже приведен пример программы, в котором демонстрируется применение ключевых слов checked и unchecked.