Скачиваний:
60
Добавлен:
24.03.2015
Размер:
303.62 Кб
Скачать

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

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

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

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

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

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

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

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

Необязательные модификаторы объявления делегата: 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). С каждой из этих ссылок можно связать экземпляр соответствующего типа делегатов. Экземпляр делегата создается конструктором делегата, обращение к которому выполняется из выражения с операцией new. При обращении к конструктору в качестве аргумента необходимо использовать имя метода с тем же типом возвращаемого значения и с той же спецификацией параметров, которые указаны в определении делегата. Этот метод в дальнейшем будет доступен для вызова через ссылку на экземпляр делегата.

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

  • метод класса (имя метода, уточненное именем класса);

  • метод объекта (имя метода, уточненное именем объекта);

  • ссылку на уже существующий в программе экземпляр делегата.

Экземпляр делегата может представлять как метод класса, так и метод

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

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

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

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

public delegate int[] Row(int num); // делегат-тип

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

public class Example

{

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

public static 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

public 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); // Явное обращение к методу

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

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

}

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

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

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