Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Podbelsky_V_V_C_Bazovy_kurs.pdf
Скачиваний:
69
Добавлен:
02.06.2015
Размер:
1.73 Mб
Скачать

340

Г л а в а 1 6

 

 

4.Назовите классы системных исключений.

5.Объясните назначение try-блока и приведите его форматы.

6.Перечислите форматы обработчиков (ловушек) исключений.

7.Когда выполняется блок завершения обработки исключений?

8.Какими средствами могут обрабатываться ошибки времени исполнения программ?

9.В чем отличие исключения от прерывания?

10.Какими средствами поддерживается перехват исключений?

11.Что происходит в случае, если исключение не перехвачено?

12.Какими средствами могут обрабатываться ошибки времени исполнения программ?

13.Каким образом можно перехватывать все исключения?

14.Каким образом можно перехватить конкретное исключение?

15.Почему возникает необходимость в генерировании исключений самой программой?

16.Может ли исключение генерироваться повторно, после того, как оно было перехвачено?

17.Каким образом отображается трассировка событий, предшествовавших возникновению исключения?

18.В каком случае обработка исключения может прекратить выполнение программы?

19.Назовите свойства класса System.Exception, которые полезны при обработке исключений.

20.Как применять исключения для управления программой?

21.Объясните назначение и возможности операции checked.

22.Перечислите конструкторы класса Exception.

23.Объясните правила применения разных форм оператора throw.

Делегаты и события

17.1. Синтаксис делегатов

Напомним, что в качестве типов в языке C# выступают: класс, структура, перечисление, интерфейс и делегат. Рассмотрим делегаты, назначение которых – представлять в программе методы (функции). Делегаты используются в .NET механизмом обработки событий.

Стандарт языка C# выделяет три этапа применения делегатов: определение (объявление) делегата как типа, создание экземпляра делегата (инстанцирование), обращение к экземпляру делегата (вызов делегата). Обратите внимание, что определение делегата не создает его экземпляр, а только вводит тип, на основе которого впоследствии могут быть созданы экземпляры делегата.

В литературе (см., например, [8]) отмечается связанная с делегатами терминологическая проблема. Если типом является класс, то экземпляр этого типа называют объектом. Для делегатов и тип, и его экземпляр часто называют одним термином. Чтобы избежать неоднозначного истолкования, нужно учитывать контекст, в котором термин «делегат» использован. При затруднениях постараемся использовать термин «тип-делегат» для обозначения типа, а экземпляр этого типа будем называть экземпляром делегата. По нашему мнению, неудачно называть экземпляр делегата объектом. Объект в программировании по установившейся традиции принято наделять участком памяти, что не свойственно экземпляру делегата. У делегата никогда нет членов-полей.

Подобно тому, как тип конкретного массива, например, long[ ], создается на основе базового класса Array, каждый делегат-тип является наследником системного класса System.Delegate. От этого базового класса каждый делегат-тип наследует конструктор и некоторые члены, о которых будет сказано позже.

Синтаксис определения (объявления) делегата-типа:

модификаторыopt delegate тип_результата имя_делегата (спецификация_параметров);

342

Г л а в а 1 7

 

 

Необязательные модификаторы объявления делегата: new, public, protected, internal, private.

delegate – служебное слово, вводящее тип делегата. тип_результата – обозначение типа значений, возвращае-

мых методами, которые будет представлять делегат. имя_делегата – выбранный программистом идентификатор

для обозначения конкретного типа делегатов. спецификация_параметров – список спецификаций парамет-​

ров тех методов, которые будет представлять делегат. Делегат-тип может быть объявлен как локальный тип класса

или структуры либо как декларация верхнего уровня в единице компиляции. Модификатор new применим только для делегатов, локализованных в классе или структуре.

Примеры определений делегатов-типов:

public delegate int[] Row(int num); public delegate void Print(int [] ar);

Эти два определения вводят два разных типа делегатов: тип с именем Row и тип с именем Print. Экземпляр делегата Row может представлять метод с целочисленным параметром, возвращающий ссылку на одномерный целочисленный массив. Объект делегата Print может представлять метод, параметр которого – ссылка на целочисленный массив. Метод, представляемый объектом делегата Print, не может возвращать никакого значения.

Так как делегат – это тип ссылок, то, определив делегат, можно применять его для создания переменных-ссылок. Синтаксис определения ссылок традиционен:

имя_делегата имя_ссылки;

Используя введенные делегаты, можно так определить ссылки:

Row delRow;

// Ссылка с типом делегата Row

Print delPrint;

// Ссылка с типом делегата Print

После такого определения ссылки delRow, delPrint имеют неопределенные значения (null). С каждой из этих ссылок можно связать экземпляр соответствующего типа делегатов. Экземпляр делегата создается конструктором делегата, обращение к

Делегаты и события

343

 

 

которому выполняется из выражения с операцией new. При обращении к конструктору в качестве аргумента необходимо использовать имя метода с тем же типом возвращаемого значения

ис той же спецификацией параметров, которые указаны в определении делегата. Этот метод в дальнейшем будет доступен для вызова через ссылку на экземпляр делегата.

Обратите внимание, что в объявлении делегата-типа нет необходимости определять его конструктор. Конструктор в определение делегата компилятор встраивает автоматически. При обращении к конструктору в качестве аргумента можно использовать:

●● метод класса (имя метода, уточненное именем класса); ●● метод объекта (имя метода, уточненное именем объекта); ●● ссылку на уже существующий в программе экземпляр

делегата.

Экземпляр делегата может представлять как метод класса, так и метод объекта. Однако в обоих случаях метод должен соответствовать по типу возвращаемого значения и по спецификации параметров объявлению делегата.

Как уже упомянуто, определение типа делегатов может размещаться в пространстве имен наряду с другими объявлениями классов и структур. В этом случае говорят о внешнем размещении определения делегата. Кроме того, определения делегатов могут входить в объявления классов и структур. В этом случае имеет место локализация делегата.

Приведем пример использования введенных выше делегатов

исоответствующих ссылок. В следующей программе нет никакой защиты от неверных данных. Например, нельзя допускать, чтобы аргумент метода series() оказался отрицательным.

// 17_01.cs Делегаты. Их внешнее определение using System;

public delegate int[] Row(int num); // елегат-тип public delegate void Print(int[] ar); // делегат-тип public class Example {

// Метод возвращает массив цифр целого числапараметра.

static public int[] series(int num)

{

int arLen = (int)Math.Log10(num) + 1; int[] res = new int[arLen];

344

Г л а в а 1 7

 

 

for (int i = arLen - 1; i >= 0; i--)

{

res[i] = num % 10; num /= 10;

}

return res;

}

// Метод выводит на экран значения элементов массива-параметра.

static public void display(int[] ar)

{

for (int i = 0; i < ar.Length; i++) Console.Write("{0}\t", ar[i]);

Console.WriteLine();

}

} //End of Example class Program

{

static void Main()

{

Row delRow; // Ссылка на делегат

Print delPrint; // Ссылка на делегат

delRow = new Row(Example.series); // Экземпляр делегата

delPrint = new Print(Example.display); // Экземпляр делегата

int[] myAr = delRow(13579); // Вызов метода через делегата

delPrint(myAr); // Вызов метода через делегата int[] newAr = { 11, 22, 33, 44, 55, 66 }; delPrint(newAr); // Вызов метода через делегата

Example.display(myAr); // Явное обращение к методу

}

}

В одном пространстве имен объявлены два делегата-типа, класс Program с методом Main() и класс Еxample с двумя статическими методами. Метод int[] series(int num) может быть представлен экземпляром делегата Row, а метод void display(int[] ar) соответствует делегату Print. В функции Main() определены ссылки delRow и delPrint, которые затем «настраиваются» на экземпляры делегатов Row и Print. При создании экземпляров

Делегаты и события

345

 

 

делегатов в качестве аргументов конструкторов использованы уточненные имена статических методов Еxample.series и Еxample. display. Теперь ссылка delRow представляет метод example. series(), а ссылка delPrint – метод Еxample.display(). Самое важное то, что к названным методам можно обращаться через соответствующие им ссылки на экземпляры делегатов. Именно это демонстрируют следующие ниже операторы программы.

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

1

3

5

7

9

 

11

22

33

44

55

66

1

3

5

7

9

 

Обратите внимание, что последняя строка результатов получена за счет явного (минуя делегат) обращения к методу Еxample.display().

Приведенные сведения о делегатах и разобранный пример не иллюстрируют особых достоинств делегатов и необходимости их присутствия в языке. Мы даже не объяснили, какие члены присутствуют в каждом делегате.

Каждое определение делегата-типа вводит новый тип ссылок, производный, как мы уже упоминали, от единого базового класса System.Delegate. От этого базового класса каждый делегат-тип наследует конструктор и ряд полезных методов и свойств. Среди них отметим:

public MethodInfo Method {get;} – свойство, представляющее заголовок метода, на который в текущий момент “настроен” экземпляр делегата.

public object Target {get;}– свойство, позволяющее определить, какому классу принадлежит тот объект, метод которого представляет экземпляр делегата. Если значение этого свойства есть null, то экземпляр делегата в этот момент представляет статический метод класса.

Добавим в приведенную выше программу операторы:

Console.WriteLine("delRow is equals {0}.",delRow.Method); Console.WriteLine("delPrint is equals {0}.", delPrint.Method);

Результат выполнения этих операторов:

delRow is equals Int32[] series(Int32). delPrint is equals Void display(Int32[]).

346 Г л а в а 1 7

Обратите внимание, что вместо типа int указано его системное обозначение Int32, а вместо void – Void.

Экземпляры делегатов представляют статические методы, поэтому выводить значения delRow.Target и delPrint.Target в нашем примере нет смысла – они равны null, и при выводе это значение заменяется пустой строкой.

17.2. Массивы делегатов

Делегаты, точнее ссылки на экземпляры делегатов, можно объединять в массивы. Такая возможность позволяет программисту задавать наборы действий, которые затем будут автоматически выполнены в указанной последовательности.

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

Так как нашей целью является только иллюстрация возможностей массивов делегатов, то максимально упростим задачу. Определим класс роботов Robot с четырьмя уже названными методами (командами) управления и методом для выдачи информации о местоположении робота. Вне класса роботов определим тип делегатов Steps, предназначенных для представления методов управления роботом. В главной программе создадим объект класса роботов и именующую его ссылку rob. Массив делегатов trace будем формировать, присваивая его элементам ссылки на безымянные экземпляры делегата, адресующие разные методы созданного объекта класса роботов. Этот массив будет имитировать последовательность команд, управляющих перемещением конкретного объекта-робота. После формирования массива переберем его элементы в цикле и с их помощью последовательно вызовем все запланированные методы, управляющие перемещением робота.

//17_02.cs массив делегатов

//Последовательность команд управления перемещением робота.

Делегаты и события

347

 

 

 

 

using System;

 

 

 

class Robot // Класс для представления робота

 

{

 

 

 

int x, y; // Положение робота на плоскости

 

public void right() { x++; }

// Направо

 

public void left() { x--; }

// Налево

 

public void forward() { y++; }

// Вперед

 

public void backward() { y--; } // Назад

 

public void position()

 

// Сообщить

 

координаты

 

 

 

{ Console.WriteLine("The Robot position: x={0}, y={1}",

x, y); }

 

 

 

}

 

 

 

delegate void Steps();

 

// Делегат-тип

 

class Program {

 

 

 

static void Main()

 

 

 

{

 

 

 

Robot rob = new Robot();

// Конкретный робот

 

Steps[] trace = { new Steps(rob.backward),

 

new Steps(rob.backward), new Steps(rob.left)};

 

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

 

{

 

 

 

Console.WriteLine("Method={0}, Target={1}",

 

trace[i].Method, trace[i].Target);

 

trace[i]();

 

 

 

}

 

 

 

rob.position();

// Сообщить координаты

 

}

 

 

 

}

 

 

 

В цикле перебора элементов массива ссылок на экземпляры делегата (trace) помещен вывод на консоль заголовка метода, адресованного очередным элементом массива. Тут же выводится имя того класса, которому принадлежит этот нестатический метод. Для получения заголовка и имени класса используются уже упомянутые свойства класса делегатов (Method и Target).

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

Method=Void backward(), Target=Robot

Method=Void backward(), Target=Robot

Method=Void left(), Target=Robot

The Robot position: x=-1, y=-2

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