Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
CSBasicCourse2ndedPodbelsky / CSBasicCourse2ndedPodbelsky.rtf
Скачиваний:
27
Добавлен:
22.03.2016
Размер:
11.9 Mб
Скачать

Глава 17. Делегаты и события

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

Напомним, что в качестве типов в языке C# выступают: класс, структура,

перечисление, интерфейс и делегат "делегат" . Рассмотрим делегаты, назначение

которых – представлять в программе методы (функции). Делегаты используются

в .NET механизмом обработки событий.

Стандарт языка C# выделяет три этапа применения делегатов: определение

(объявление) делегата как типа, создание экземпляра делегата (инстанцирование

"инстанцирование"

),

обращение

к

экземпляру

делегата

(вызов

делегата

"делегат:вызов делегата" ). Обратите внимание, что определение делегата

"делегат:определение делегата" не создает его экземпляр, а только вводит тип, на

основе которого впоследствии могут быть созданы экземпляры делегата.

В литературе (см., например, [8]) отмечается связанная с делегатами

терминологическая проблема. Если типом является класс, то экземпляр этого типа

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

термином. Чтобы избежать неоднозначного истолкования, нужно учитывать

контекст, в котором термин "делегат" использован. При затруднениях постараемся

использовать термин тип-делегат для обозначения типа, а экземпляр этого типа

будем называть экземпляром делегата. По нашему мнению неудачно называть

экземпляр делегата объектом. Объект в программировании по установившейся

традиции принято наделять участком памяти, что не свойственно экземпляру

делегата. У делегата никогда нет членов, да нет и тела, ограниченного фигурными

скобками.

Подобно тому, как тип конкретного массива, например, long[ ], создаётся на

основе базового класса Array, каждый делегат-тип является наследником системного

класса System.Delegate "класс: System.Delegate" . От этого базового класса кажды

й

делегат-тип наследует конструктор и некоторые члены, о которых чуть позже.

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

"объявления: делегата-

типа" :

модификаторыopt delegate тип_результата

имя_делегата_типа (спецификация_параметров);

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

internal, private.

delegate – служебное слово

"служебное слово: 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).

С

каждой

из

этих

ссылок

можно

связать

экземпляр

соответствующего типа делегатов. Экземпляр делегата

"делегат:экземпляр

делегата" создается конструктором делегата, обращение к которому выполняется

из выражения с операцией 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];

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. При создании экземпляров делегатов в качестве аргументов конструкторов

использованы

уточненные

имена

статических

методов

Е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:} – свойство

"свойство: Method" ,

представляющее заголовок метода, на который в текущий момент "настроен"

экземпляр делегата

.

public object Target {get:}– свойство

"свойство: Target" , позволяющее

определить какому классу принадлежит тот объект, метод которого представляет

экземпляр делегата. Если значение этого свойства есть 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[ ]).

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

Int32, а вместо void – Void.

Экземпляры делегатов представляют статические методы, поэтому выводить

значения delRow.Target и delPrint.Target в нашем примере нет смысла – они равны

null, и при выводе это значение заменяется пустой строкой.

Соседние файлы в папке CSBasicCourse2ndedPodbelsky