
- •Введение
- •Сравнение языков С++ и C#
- •Логические выражения
- •Функции для ввода и вывода в языке C#
- •Управление форматом числовых данных:
- •Обработка исключительных ситуаций
- •Методы и модификаторы параметров
- •Неявно типизированные переменные
- •Понятие класса
- •Свойства
- •Индексаторы
- •Одномерные индексаторы
- •Многомерные индексаторы
- •Перегрузка методов
- •Перегрузка знаков операций
- •Наследование
- •Виртуальные функции
- •Работа с файлами
- •Работа с каталогами
- •Абстрактный класс FileSystemInfo
- •Класс DirectoryInfo
- •Сериализация
- •FileSystemWatcher – отслеживание событий, связанных с файлами
- •Обобщения (шаблоны)
- •Интерфейсы
- •Коллекции
- •LINQ
- •Грамматика выражений запросов
- •Синтаксис запросов
- •Проекция и фильтрация
- •Упорядочение
- •Агрегирующие запросы
- •Операции с коллекциями
- •Операция Concat
- •Операция Union
- •Преобразование
- •Объединение последовательностей
- •FirstOrDefault
- •Группировка
- •Групповая адресация
- •Обработка событий
- •Групповое преобразование делегируемых методов
- •Применение методов экземпляра в качестве делегатов
- •Групповая адресация
- •Ковариантность и контравариантность
- •Класс System. Delegate
- •Назначение делегатов
- •Анонимные функции
- •Анонимные методы
- •Передача аргументов анонимному методу
- •Возврат значения из анонимного метода
- •Применение внешних переменных в анонимных методах
- •Лямбда-выражения
- •Лямбда-оператор
- •Одиночные лямбда-выражения
- •Блочные лямбда-выражения
- •События
- •Пример групповой адресации события
- •Применение аксессоров событий
- •Разнообразные возможности событий
- •Применение анонимных методов и лямбда-выражений вместе с событиями
- •Рекомендации по обработке событий в среде .NET Framework
- •Применение делегатов EventHandler<TEventArgs> и EventHandler
- •Практический пример обработки событий

Чернов Э. А. |
- 115 - |
Лекции по языку C# v 2.3 |
Связь делегата с методом можно упростить посредством так называемого группового преобразования методов, при котором опускается операция new, и явно указывается имя метода. Форма объявления переменной типа делегата получает вид:
ArProc ap = Calc_Sum;
Следует подчеркнуть, что объекту типа делегата должна присваиваться ссылка, а не возвращаемое методом значение. Возврат значения произойдет, если после имени метода записать круглые скобки (пустые или со списком параметров).
Объект типа делегата посредством ссылки на объект может обращаться к методам этого объекта. Для обеспечения возможности такой ссылки класс, содержащий требуемые методы, должен объявляться с атрибутом public. Изменения в приведенном выше примере:
Заголовок объявления класса.
public class DelegateTest
В основной подпрограмме Main (применяя групповое преобразование и совмещая объявление объекта типа делегата со ссылкой на метод):
ArProc ap = DelegateTest.Calc_Sum;
Групповая адресация
Делегаты предоставляют возможность создать список, состоящий из делегата с переданными ему последовательно несколькими методами. Такой список называется групповой адресацией и позволяет создать цепочку вызовов методов, которые вызываются автоматически при обращении к делегату. Для добавления в цепочку делегата с другим методом применяют оператор +=, а для удаления оператор -=.
Методы из цепочки выполняются последовательно, и если эти методы возвращают значение, то смысла в таком возврате нет. (возвращаемое значение метода входит в сигнатуру делегата). Возвращено будет значение только для метода из последнего делегата в цепочке. Поэтому делегат, в котором используется групповая адресация, обычно имеет возвращаемый тип void.
Ниже приведен пример из книги ProfessorWEB (Руководство по C# Часть 2). Для возврата измененной строки в вызывающую часть кода служит параметр типа ref. Благодаря этому методы оказываются более приспособленными для групповой адресации.
delegate void OpStroke(ref int[ ] arr); public class ArrOperation
{
public static void WriteArray(ref int[ ] arr)
{
Console.WriteLine("Исходный массив: "); foreach (int i in arr)
Console.Write("{0}\t", i); Console.WriteLine();
Чернов Э. А. |
- 116 - |
Лекции по языку C# v 2.3 |
}
// Сортировка массива
public static void IncSort(ref int[ ] arr)
{
int j, k;
for (int i = 0; i < arr.Length - 1; i++)
{
j = 0; do
{
if (arr[j] > arr[j + 1])
{
k = arr[j];
arr[j] = arr[j + 1]; arr[j + 1] = k;
}
j++;
}
while (j < arr.Length - 1);
}
Console.WriteLine("Отсортированный массив по возрастанию: "); foreach (int i in arr)
Console.Write("{0}\t", i); Console.WriteLine();
}
public static void DecSort(ref int[] arr)
{
int j, k;
for (int i = 0; i < arr.Length - 1; i++)
{
j = 0; do
{
if (arr[j] < arr[j + 1])
{
k = arr[j];
arr[j] = arr[j + 1]; arr[j + 1] = k;
}
j++;
}
while (j < arr.Length - 1);
}
Console.WriteLine("Отсортированный массив по убыванию: "); foreach (int i in arr)
Console.Write ("{0}\t", i); Console.WriteLine();
Чернов Э. А. |
- 117 - |
Лекции по языку C# v 2.3 |
}
// Заменяем нечетные числа четными и наоборот public static void ChetArr(ref int[] arr)
{
Console.WriteLine("Четный массив: "); for (int i = 0; i < arr.Length; i++)
if (arr[i] % 2 != 0) arr[i] += 1;
foreach (int i in arr) Console.Write("{0}\t", i);
Console.WriteLine();
}
public static void NeChetArr(ref int[] arr)
{
Console.WriteLine("Нечетный массив: "); for (int i = 0; i < arr.Length; i++)
if (arr[i] % 2 == 0) arr[i] += 1;
foreach (int i in arr) Console.Write("{0}\t", i);
Console.WriteLine();
}
}
class Program
{
static void Main()
{
int [ ] myArr = new int[6] { 2, -4, 10, 5, -6, 9 };
//Структурируем делегаты
OpStroke Del;
OpStroke Wr = ArrOperation.WriteArray; OpStroke OnSortArr = ArrOperation.IncSort; OpStroke OffSortArr = ArrOperation.DecSort; OpStroke ChArr = ArrOperation.ChetArr; OpStroke NeChArr = ArrOperation.NeChetArr;
//Групповая адресация
Del = Wr;
Del += OnSortArr; Del += ChArr; Del += OffSortArr; Del += NeChArr;
// Выполняем делегат
Del(ref myArr); Console.ReadLine();
Чернов Э. А. |
- 118 - |
Лекции по языку C# v 2.3 |
}
}
Цепочки вызовов наиболее широко применяются для обработки событий.
Анонимные методы и лямбда-выражения
При объявлении объекта делегата описывается сигнатура делегата, а метод предварительно должен быть создан. Если в делегате используется единственный метод, то допускается сокращенное оформление метода вместе с делегатом. Такое оформление называется анонимным методом. Синтаксис объявления объекта делегата с анонимным методом сохраняется. Разница возникает при оформлении этого делегата.
Ниже представлен пример программы, в которой делегат принимает единственный метод, вычисляющий число Фиббоначи с заданным номером. Сначала приведен пример классического оформления делегата.
class Program
{
delegate int Fib (int end);
public static int Fib_Calc(int num)
{
int prev = 1, curr = 1, next;
for (int k = 3; k <= num; ++k)// Цикл до введенного номера
{
next = prev + curr; |
// Очередное число |
prev = curr; |
// Перестановка |
curr = next; |
|
} |
|
return curr; |
|
} |
|
class AnonimDemo |
|
{ |
|
static void Main() |
|
{ |
|
int n = 7; //
// Объявление переменной типа делегата
Fib fb = Fib_Calc;
Console.WriteLine("Число с номером {0} = {1} ", n, fb(n);); Console.ReadKey();
}
}
Оформление той же программы с применением анонимного метода. Обратите внимание: вместо объявления переменной типа делегата использовано имя метода. После слова delegate указан список параметров, а затем оформлен метод, заканчивающийся точкой с запятой.
class Program
{
Чернов Э. А. |
- 119 - |
Лекции по языку C# v 2.3 |
delegate int Fib (int end); class AnonimDemo
{
static void Main()
{
int n = 7; //
// Оформление анонимного метода, num = № числа
Fib Fib_Calc = delegate (int num)
{
int prev = 1, curr = 1, next;
for (int k = 3; k <= num; ++k)// Цикл до введенного номера
{
next = prev + curr; |
// Очередное число |
prev = curr; |
// Перестановка |
curr = next; |
|
} |
|
return curr; |
|
};
Console.WriteLine("Число с номером {0} = {1} ", n, Fib_Calc(n)); Console.ReadKey();
}
}
}
Для анонимных методов предложен упрощенный метод оформления, названный лямбда-выражениями. Так в предыдущем примере для перехода к лямбда-выраже- ниям достаточно строку:
Fib Fib_Calc = delegate (int num)
Заменить строкой:
Fib Fib_Calc = num =>
После знака '=>' записывается одиночный оператор или блок операторов в фигурных скобках, а перед этим знаком записывается имя одиночного параметра (если он один), а если параметров несколько, то они записываются в круглых скобках через запятую.
Если перед классом AnonimDemo описать делегат: delegate double Prod(double x, double y);
То лямбда-выражение можно оформить так:
Prod pr = (x, y) => 2* x * y; |
// Оформление лямбда-выражения |
Console.WriteLine(pr(3.5, 2.5) ) ; |
// Вызов делегата |
Делегаты могут использоваться в качестве параметров при вызове методов. В примере на стр. 81 был вызван метод поиска Find для списка. В качестве параметра для указания условий поиска указан делегат. Общий фрагмент оформления лямдавыражения имеет вид:
// Объявление блока и списка
Block blk = new Block();