Лаб.раб №14
Делегаты и события
Содержание
1 Делегаты …………………………………………………………… 2
1.1 Ссылка делегата на статические методы …………………… 2
№ 1…………………………………………………………. 2
1.2 Ссылка делегата на методы экземпляров класса…………… 4
№ 2………………………………………………………….. 4
1.3 Многоадресная арифметика…………………………………. 6
№ 3…………………………………………………………. 6
2 События……………………………………………………………. 7
2.1 Пример применения постейшего события………………….. 8
№ 4…………………………………………………………. 8
-
Работа с событием для многоадресной передачи………….. 9
№ 5………………………………………………………….. 9
-
Методы экземпляров класса как обработчики событий…… 10
№ 6………………………………………………………….. 10
№ 7…………………………………………………………. 11
-
Использование событийных средств доступа……………… 12
№ 8………………………………………………………….. 12
2.5 Особенности обработки событий в среде .NET Framework.. 14
№ 9………………………………………………………….. 14
-
Применение всироенного делегата EventHandler………….. 15
№ 10………………………………………………………… 15
-
Пример создания разработчика событий нажатия клавиш… 16
№ 11 ………………………………………………………. 16
1 Делегаты
Делегат – это объект, который может ссылаться на метод в процессе выполнения программы. Делегат может вызывать метод, на который он ссылается. Делегат объявляется с помощью ключевого слова delegate. Общая фома объявления следующая:
delegate тип_возврата имя_делегата(список_параметров);
Делегат может вызывать только такие методы, у которых тип возвращаемого значения и список параметров (т.е. его сигнатура) совпадают с соответствующими элементами объявления делегата.
Делегат может вызывать либо метод экземпляра класса, связанный с объектом, или статческий метод, связанный с классом. Рассмотрим простой пример.
1.1 Ссылка делегата на статические методы
№ 1
// Первая простая пограмма с делегатом
using System;
// Объявление делегата
delegate string strMod(string str);
class DelegateTest
{
// Метод замеяет пробелы дефисами.
static string replaceSpaces(string a)
{
Console.WriteLine("\n Замена пробелов дефисами.");
return a.Replace(' ', '-');
}
// Метод удаляет побелы.
static string removeSpaces(string a) {
string temp = "";
int i;
Console.WriteLine(" Удаление пробелов.");
for (i = 0; i < a.Length; i++)
if (a[i] != ' ') temp += a[i];
return temp;
}
// Метод реверсирует строку
static string reverse(string a) {
string temp = "";
int i,j;
Console.WriteLine(" Реверсирование строки.");
for(j=0, i=a.Length-1; i >= 0; i--,j++)
temp += a[i];
return temp;
}
public static void Main()
{
// Создание делегата.
strMod strOp = new strMod(replaceSpaces);
string str;
// Вызываем методы посредством делегата.
str = strOp(" Это простой тест.");
Console.WriteLine(" Результирующая строка: " + str);
Console.WriteLine();
strOp = new strMod(removeSpaces);
str = strOp(" Это простой тест.");
Console.WriteLine(" Результирующая строка: " + str);
Console.WriteLine();
strOp = new strMod(reverse);
str = strOp(" Это простой тест.");
Console.WriteLine(" Результирующая строка: " + str);
Console.WriteLine("\n\n\n ");
}
}
Рис 1 Результаты работы первой программы с делегатами
Отметим ряд особнностей работы с делегатом
1 В программе делегату дано имя strMod (имя выбирается программистом). Это имя используется как имя метода, который получает один параметр типа string и возвращает одно значение типа string-значения.
2 В функции Main() составляется основное уравнение, связывающее имя делегата strMod с созданием ссылки c именем strOp на метод, выполняющем требуемое преобразование, поручаемое делегату. Представим это уравнение таким образом:
(имя делегата) (имя ссылки) = new (имя делегата) (имя метода преобразования);
D reference = new D (reform());
Мы получили классическую в С# зависимость между ссылкой и методом, выполняющим требуемое преобразование.
-
Структура метода reform() имеет такой вид
// комментарий о конкретном выполняемом преобразовании
static string reform (string a) {
string temp = f(a) ;
return temp ;
}
4 f(a) – способ формирования (создания) выходного string-значения. Это может быть один из известных методов класса string (в рассмотренном выше примере это метод Replace() - a.Replace(‘ ‘, ‘-‘); ) или отдельные решения, отвечающие поставленным требованиям (в рассмотренной задаче – это string temp = a[i];).
5 Создается впечатление, что одно имя делегата может быть использовано для множества преобразований.
6 Теперь можно согласиться, что делегат это объект, который может ссылаться на метод и этод метод можно вызвать посредством соответствующей ссылки, т.е. может вызвать метод, на который он ссылается. Ссылка на объект это адрес объекта. Таким образом, метод, который будет вызван делегатом, определяется не в период компиляции программы, а во время ее работы. Общая форма объявления делегата имеет следующий вид:
delegate тип_возврата имя(список_параметров);
7 Делегат может вызвать только такие методы, у которых тип возврощаемого значения и список параметров (т.е. его сигнатура) совпадают с соответствующими элементами объявления делегата.
8 Делегат может ссылаться на методы экземпляров класса, но при этом он должен использовать объктную ссылку.
Ниже приводится текст предыдущей программы, переписанный с целью инкапсуляци операций над строками внутри класса StringOps.
1.2 Ссылка делегата на экземпляры класса
№ 2 - предыдущая программа, но делегаты ссылаются на методы экземпляров класса.
// Делегаты ссылаются на методы экземпляров кдасса
using System;
// Объявление делегата
delegate string strMod(string str);
class StringOps
{
// Метод замеяет пробелы дефисами.
public string replaceSpaces(string a)
{
Console.WriteLine("\n Замена пробелов дефисами.");
return a.Replace(' ', '-');
}
// Метод удаляет пробелы.
public string removeSpaces(string a)
{
string temp = "";
int i;
Console.WriteLine(" Удаление пробелов.");
for (i = 0; i < a.Length; i++)
if (a[i] != ' ') temp += a[i];
return temp;
}
// Метод реверсирует строку
public string reverse(string a)
{
string temp = "";
int i, j;
Console.WriteLine(" Реверсирование строки.");
for (j = 0, i = a.Length - 1; i >= 0; i--, j++)
temp += a[i];
return temp;
}
}
class DelegateTest {
public static void Main()
{
// Создается экземпляр класса StringOps.
StringOps so = new StringOps();
// Создаем делегат.
strMod strOp = new strMod( so.replaceSpaces );
string str;
// Вызываем методы с использованием делегатов.
str = strOp(" Это простой тест.");
Console.WriteLine(" Результирующая строка: " + str);
Console.WriteLine();
strOp = new strMod( so.removeSpaces );
str = strOp(" Это простой тест.");
Console.WriteLine(" Результирующая строка: " + str);
Console.WriteLine();
strOp = new strMod( so.reverse );
str = strOp(" Это простой тест.");
Console.WriteLine(" Результирующая строка: " + str);
Console.WriteLine("\n\n\n ");
}
}
Рис 2 Повторение результатов задачи № 1
1.3 Многоадресатная передача
Особенность делегата – поддержка многоадресатной передачи (multicasting). Многоадресатная пердача – это способность создавать список вызовов (или цепочку вызовов) методов, которые должны автоматически вызываться при вызове делегата. Для создания такой цепочки достаточно создать экземпляр делегата, а затем для добавления методов в эту цепочку использовать операто «+=». Для удаления метода из цепочки используется оператор «-=» .
№ 3
// Применение многоадресатной пердачи
using System;
// Объявляем делегат.
delegate void strMod(ref string str);
class StringOps
{
// Метод заменяет пробелы дефисами.
static void replaceSpaces(ref string a)
{
Console.WriteLine("\n Замена пробелов дефисами.");
a = a.Replace(' ', '-');
}
//Метод удаляет пробелы.
static void removeSpaces(ref string a)
{
string temp = "";
int i;
Console.WriteLine(" Удаление пробелов.");
for (i = 0; i < a.Length; i++)
if (a[i] != ' ') temp += a[i];
a = temp;
}
// Метод реверсирует строку.
static void reverse(ref string a) {
string temp = "";
int i, j;
Console.WriteLine(" Реверсирование строки.");
for(j =0, i=a.Length -1; i >= 0; i--, j++)
temp += a[i];
a = temp;
}
public static void Main() {
// Создаем экземпляры делегатов.
strMod strOp;
strMod replaceSp = new strMod(replaceSpaces);
strMod removeSp = new strMod(removeSpaces);
strMod reverseStr = new strMod(reverse);
string str = " Это простой тест.";
// Организация многоадресатной передачи.
strOp = replaceSp;
strOp += reverseStr;
// Вызов делегата многоадресатной передачи.
strOp(ref str);
Console.WriteLine(" Результирующая строка: " +str);
Console.WriteLine();
// Удаляем метод замены пробелов и
// добавляем метод их удаления.
strOp -= replaceSp;
strOp += removeSp;
str = " Это простой тест."; // Восстановление исходной строки.
// Вызов делегата многоадресатной передачи.
strOp(ref str);
Console.WriteLine(" Результирующая строка: " + str);
Console.WriteLine("\n\n\n ");
}
}
Рис 3 Результат применения многоадресатной пердачи
Основные выводы о важности применения делегатов:
1 Делегаты обеспечивают поддержку функционирования событий.
2 Делегаты позволяют во время выполнения программы выполнить метод, который точно не известен в период компляции. Это особенно полезно для подключения программных компонент к создаваемым оболочкам.