Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Лекции по ООП (язык C#).pdf
Скачиваний:
190
Добавлен:
16.05.2015
Размер:
1.54 Mб
Скачать

Чернов Э. А.

- 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();