Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Ответы_ТП

.pdf
Скачиваний:
9
Добавлен:
21.05.2015
Размер:
880.4 Кб
Скачать

с.show();

Console.WriteLine();

с = с - b; // Вычитаем b из с.

Console.Write ("Результат вычитания с - b: " ) ;

с.show();

Console.WriteLine();

}}

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

Координаты точки а: 1, 2, 3

Координаты точки b: 10, 10, 10

Результат сложения а + b: 11, 12, 13

Результат сложения а + b + с : 22, 24, 26

Результат вычитания с - а: 21, 22, 23

Результат вычитания с - b : 11, 12, 13

При воздействии оператора " + " на два объекта типа ThreeD величины соответствующих координат суммируются, как показано в методе operator+(). Однако заметьте, что этот метод не модифицирует значения ни одного из операндов. Этот метод возвращает новый объект типа ThreeD, который содержит результат выполнения рассматриваемой операции. Это происходит и в случае стандартного арифметического оператора сложения "+", примененного, например, к числам 10 и 12. Результат операции 10+12 равен 22, но при его получении ни 10, ни 12 не были изменены. Хотя не существует правила, которое бы не позволяло перегруженному оператору изменять значение одного из его

операндов, все же лучше, чтобы он не противоречил общепринятым нормам. Обратите внимание на то, что метод operator+() возвращает объект типа ThreeD.

Несмотря на то что он мог бы возвращать значение любого допустимого в С# типа, тот факт, что он возвращ ает объект типа ThreeD, позволяет использовать оператор "+" в таких составных выражениях, как а+b+с Здесь часть этого выражения, а+b, генерирует результат типа ThreeD, который затем суммируется с объектом с. И если бы выражение генерировало значение иного типа (а не типа ThreeD), такое составное выражение попросту не работало бы. При сложении координат внутри метода operator+() выполняется целочисленное сложение, поскольку отдельные координаты представляют собой целочисленные величины. Факт перегрузки оператора "+" для объектов типа ThreeD не влияет на оператор "+", применяемый к целым числам.

Теперь рассмотрим операторный мето д operator-(). Оператор "-" работает подобно оператору "+" за исключением того, что здесь важен порядок следования операндов. Вспомните, что сложение коммутативно, а вычитание — нет (т.е. А—В не то же самое, что В—А). Для всех бинарных операторов первый па раметр операторного метода будет содержать левый операнд, а второй параметр — правый. При реализации перегруженных версий некоммутативных операторов необходимо помнить, какой операнд является левым, а какой — правым.

4.3.2 Перегрузка унарных операторов

Унарные операторы перегружаются точно так же, как и бинарные. Главное отличие, конечно же, состоит в том, что в этом случае существует только один операнд. Рассмотрим, например, метод, который перегружает унарный " минус" для класса ThreeD.

// Перегрузкаунарногооператора "-".

public static ThreeD operator -(ThreeD op)

{ThreeD result = new ThreeD();

result.x = -op.x;

result.у = -op.у;

result. z = -op. z;

return result;}

Здесь создается новый объект, который содержит поля операнда, но со знаком "минус". Созданный таким образом объект и возвращается операторным методом operator-(). Обратите внимание на то, что сам операнд остается не модифицированным. Такое поведение соответствует обычному действию унарного "минуса". Например, в выражении

а= -b

аполучает значение b, взятое с противоположным знаком, но само b при этом не меняется.

Однако в двух случаях операторный метод изменяет содержимое операнда. Речь идет об операторах инкремента (++) и декремента ( -- ). Поскольку обычно эти операторы выполняют функции инкрементирования и декрементирования значений, соответственно, то перегруженные операторы "+" и " - " , как правило, инкрементируют свой операнд. Таким образом, при пер егрузке этих операторов операнд обычно модифицируется. Например, рассмотрим метод operator++ () для класса ThreeD.

// Перегрузкаунарногооператора "++".

public static ThreeD operator ++(ThreeD op)

{// Оператор "++" модифицирует аргумент.

op.x++;

op.y++;

op.z++;

return op;}

Обратите внимание: в результате выполнения этого операторного метода объект, на который ссылается операнд ор, модифицируется. Итак, операнд, подвергнутый операции "++", инкрементируется. Более того, модифицированный объект возвращается эти м методом, благодаря чему оператор "++" можно использовать в более сложных выражениях.

36. Перехват всех исключений, Вложение try -блоков

Иногда требуется перехватывать все исключения, независимо от их типа. Для этого используйте catch-инструкцию без параметров. В этом случае создается обработчик "глобального перехвата", который используется, чтобы программа гарантированно обработала все исключения. В следующей программе приведен пример использования такого обработчика, который успешно перехватыва ет генерируемые здесь исключение типа IndexOutOfRangeException и исключение типа

DivideByZeroException .

//Использование catch-инструкции для “глобального перехвата".

using System;

class ExcDemo4 {

public static void Main() {

// Здесь массив numer длиннее массива denoiti.

int[] numer = { 4, 8, 16, 32, 64, 128, 256, 512 };

int[] denom = { 2 , 0, 4, 4, 0, 8 } ;

for(int i=0; i < numer.Length; i++){

try {

Console.WriteLine(numer[i] + " / " +

denom[i] + " равно " +

numer[i]/denom[i]);

}

catch {

Console.WriteLine(

"Произошло некоторое исключение.");

}}}}

Вот как выглядят результаты выполнения этой программы:

4/2

равно 2

Произошло некоторое исключение.

16/4 равно 4

32/4 равно 8

Произошло некоторое исключение.

128/8 равно 16

Произошло некоторое исключение .

Произошло некоторое исключение.

7.5 Вложение try-блоков

Один try-блок можно вложить в другой. Исключение, сгенерированное во внутреннем try-блоке и не перехваченное catchинструкцией, которая связана с этим try-блоком, передается во внешний try-блок. Например, в следующей программе исключение типа

IndexOutOfRangeException перехватывается не внутренним tryблоком,

а внешним.

usingSystem; // Использование вложенного try-блока, class NestTrys {

public static void Main() {

// Здесь массив numer длиннее массива denom. int[] numer = { 4, 8, 16, 32, 64, 128, 256, 512 }; int[] denom = { 2, 0, 4, 4, 0, 8 };

try { // Внешний try -блок.

for(int i=0; i < numer.Length; i++) { try { // Вложенный try -блок

Console.WriteLine(numer[i] + " / " +denom[i] + " рав но " + numer[i]/denom[i]);

}

catch (DivideByZeroException) {// Перехватываем исключение

Console.WriteLine(" Нанульделитьнельзя!");

}}}

catch (IndexOutOfRangeException) {

// Перехватываем исключение.

Console.WriteLine("Нет соответствующего элемента.");

Console.WriteLine(

"Неисправимая ошибка — программа завершена.");

}}}

Вот результаты выполнения этой программы:

4/2

равно 2

На нуль делить нельзя!

16/4 равно 4

32/4 равно 8

На нуль делить нельзя!

128/8 равно 16

Нет соответствующего элемента.

Неисправимая ошибка — программа завершена.

Многие программисты используют внешний try-блок для перехвата самых серьезных ошибок, позволяя внутренним try-блокам обрабатывать менее опасные. Внешние try-блоки можно также использовать в качестве механизма "глобального пер ехвата" для обработки тех ошибок, которые не перехватываются внутренним блоком

37.Перехват исключений производных классов

При перехвате исключений, типы которых включают базовые и производные классы, необходимо обращать внимание на порядок catch- инструкций, поскольку catch-инструкция для базового класса соответствует любой catch-инструкции производных классов. Например, поскольку базовым классом для всех исключений является Exception , при перехвате исключения типа Exception будут перехватываться все возможные исключения. Конечно, использование catch-инструкции без аргумента обеспечивает более ясный способ перехвата всех исключений. Однако проблема перехвата исключений производных классов очень важна в других контекстах, особенно в случае создания собственных классов исключений.

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

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

Следующая программа создает два класса исключений с именами ExceptA и ExceptB. Класс ExceptA выводится из класса

ApplicationException , а класс ExceptB — из класса ExceptA. Затем программа генерирует исключение каждого типа.

//Инструкции перехвата исключений производных классов

//должны стоять перед инструкциями перехвата исключений

//базовых классов.

usingSystem;

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

class ExceptA : ApplicationException { public ExceptA() : base() { }

public ExceptA(string str) : base(str) { } public override string ToStringO { return Message;

}}

//Создаем класс исключения как производный

//от класса ExceptA.

class ExceptB : ExceptA { public ExceptB() : base() { }

public ExceptB(string str) : base(str) { } public override string ToStringO { return Message;

}}

class OrderMatters { public static void Main() { for(int x = 0; x < 3; x++) { try {

if(x==0) throw new ExceptA(

"Перехват исключения типа ExceptA."); else if(x==l) throw new ExceptB(

"Перехват исключени я типа ExceptB."); else throw new ExceptionO;

}

catch (ExceptBexc) {

//Перехватываемисключение . Console.WriteLine(exc);

}

catch (ExceptAexc) {

//Перехватываемисключение . Console.WriteLine(exc);

}

catch (Exception exc) { Console.WriteLine(exc);

}}}}

Вот результаты выполнения этой программы:

Перехват исключения типа ExceptA.

Перехват исключения типа ExceptB.

System.Exception: Exception of type System.Exception was thrown,

atOrderMatters.Main()

Обратите внимание на порядок следования catch -инструкций. Это

— единственно правильный вариант. Поскольку класс ExceptB выведен из класса ExceptA, catch -инструкция для исключений типа ExceptB должна стоять перед инструкцией, предназначенной для перехвата исключений типа ExceptA. Точно так же catch -инструкция дл я исключений класса Exception (который является базовым для всех исключений) должна стоять последней. Чтобы убедиться в этом, попробуйте переставить catch-инструкции в другом порядке. Это приведет к ошибке при компиляции.

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

38.Присваивание и сравнение объектов

Механизм выполнения присваивания один и тот же для вел ичин любого типа, как ссылочного, так и значимого, однако результаты различаются. При присваивании значения копируется значение, а при присваивании ссыл ки — ссылка, поэтому после присваивания одного объекта другому мы получим две ссылки, указывающие на од ну и ту же область памяти (рис. 1.2).

Рисунок иллюстрирует ситуацию, когда было создано три объекта, а, b и с, а за тем выполнено присваивание b = с. Старое значение b становится недоступным и очищается сборщиком мусора. Из этого

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]