Помощь в написании студенческих работ
Антистрессовый сервис

Анонимные методы и лямбда-выражения

РефератПомощь в написанииУзнать стоимостьмоей работы

Делегат-тип Cast предназначен для представления методов с параметром типа double, возвращающих значение типа int. В методе Main () объявлены два экземпляра делегата Cast, ассоциированные со ссылками castl и cast2. Для инициализации ссылок вместо явного обращения к конструктору делегата Cast используются анонимные методы, каждый из которых вводится с помощью служебного слова delegate. Анонимный… Читать ещё >

Анонимные методы и лямбда-выражения (реферат, курсовая, диплом, контрольная)

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

Формат декларации анонимного метода:

delegate (спецификация_параметров)

{операторы_тела_метода};

Компилятор, «встретив» декларацию анонимного метода, формирует экземпляр делегата, и настраивает его на выполнение операторов тела анонимного метода. Применение анонимных методов рекомендуется в тех случаях, когда метод используется однократно или его вызовы локализованы в одном блоке кода программы.

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

// 17−05.cs — анонимные методы…

using System;

delegate int Cast (double x); // Объявление делегата-типа

class Program.

{

static void Main ().

{

double test = 15.3;

Cast castl = delegate (double z) // ближайшее целое

{ return (int)(z + 0.5); };

Cast cast2 = delegate (double z) 11 большее целое { return ((int)z == z? (int)z: (int)(z + 1)); };

Console.WriteLine («castl (test)={0}, cast2(test)= {1}», castl (test), cast2(test));

Console.WriteLine («castl (44.0)={0}, cast2(44.0)= {1}», castl (44.0), cast2(44.0));

}

}

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

castl (test)=15, cast2(test)= 16.

castl (44.0)=44, cast2(44.0)= 44.

Делегат-тип Cast предназначен для представления методов с параметром типа double, возвращающих значение типа int. В методе Main () объявлены два экземпляра делегата Cast, ассоциированные со ссылками castl и cast2. Для инициализации ссылок вместо явного обращения к конструктору делегата Cast используются анонимные методы, каждый из которых вводится с помощью служебного слова delegate. Анонимный метод, связанный со ссылкой castl, получив в качестве параметра вещественное значение, возвращает ближайшее к нему целое число. Анонимный метод, представляемый ссылкой cast2, возвращает целое число, не меньшее значения параметра.

Более удобным (с точки зрения краткости и выразительности) средством для кодирования методов, представляемых экземплярами делегатов, являются лямбда-выражения. Лямбда-выражения появились в языке C# позже анонимных методов, но практически полностью их заменяют, так как обладают большими выразительными возможностями и краткостью, нежели анонимные методы.

Общая форма записи лямбда-выражений:

(параметры) => блок операторов

Здесь параметры — это спецификация параметров, соответствующая требованиям к спецификации параметров в типе делегата.

Лексему «=>» называют лямбда-операцией. Именно эта операция является неотъемлемой частью лямбда-выражения.

Вслед за лямбда-операцией размещен блок операторов (тело лямбдавыражения). К этому блоку требования те же, что и к телу метода. Если протокол, декларируемый типом делегата, предусматривает возвращение результата, отличного от void, то для выхода из тела лямбда-выражения используются операторы return с выражениями соответствующего типа. Таким образом, возвращаемое из тела лямбда-выражения значение и типы параметров соответствуют протоколу, определяемому делегатом.

Для формирования экземпляра делегата delegate int Cast (double x); можно записать, например, такое лямбда-выражение:

(double z) => {return (int)(z + 0.5);}.

Возможны следующие варианты упрощения записи лямбда-выражений:

  • 1) список параметров можно использовать без указания их типов;
  • 2) если параметр один, то круглые скобки можно опустить;
  • 3) если в блоке операторов только один оператор, то фигурные скобки можно опустить;
  • 4) если в блоке операторов только один оператор и это оператор return выражение, то и служебное слово return (и фигурные скобки) можно опустить. В этом случае тело лямбда-выражения упрощается до отдельного выражения.

С учетом перечисленных сокращений приведенное выше лямбдавыражение принимает такой вид:

z => (int)(z + 0.5).

Обратим внимание, что соответствие между параметрами лямбдавыражения и спецификацией параметров делегата-типа устанавливается по их взаимному расположению (т. е. позиционно, имена параметров не учитываются).

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

// 17_о — Pair — Делегаты и лямбда-выражения using System;

public delegate void Pair (string s, int d); // делегат-тип class Program {.

static void MainQ.

{

Pair birthday = (month, date) =>

Console.WriteLine («Month = {0}; Date = {1}», month, date);

Pair person = (name, year) =>

{

DateTime moment = DateTime. Now; int age = moment. Year — year;

Console.WriteLine («Name: {0}; Age: {1}», name, age);

};

person («Charley», 1967);

birthday («December», 22);

} }

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

Name: Charley; Age: 52 Month = December; Date = 22.

Делегат-тип Pair «настроен» на представления методов с двумя параметрами типов string и int, не возвращающих результат в точку вызова.

Инициализация ссылок birthday и person, имеющих тип делегататипа Pair, выполнена с помощью лямбда-выражений.

В параметрах лямбда-выражения для переменной person задаются имя и год рождения некой персоны. В теле этого лямбда-выражения несколько операторов. Обращением к свойству DateTime.Now.Year определяется числовое значение текущего года и вычисляется возраст персоны в настоящий момент. Затем имя и возраст персоны выводятся на экран.

В теле лямбда-выражения для переменной birthday один оператор. Он выводит число и месяц какого-то события (наверное, это день рождения Чарли).

Анонимные методы и лямбда-выражения удобно применять для «настроек» библиотечных методов, предусматривающих применение обратного вызова. В гл. 9, посвященной методам С#, рассмотрено применение функций обратного вызова в обращениях к библиотечному методу сортировки элементов массива. Там мы использовали имена методов в качестве аргументов метода Array. Sort (). Если обратиться к документации, то увидим, что заголовок этого метода включает параметр с типом делегата. Поэтому замещать этот параметр можно и анонимными методами, и лямбда-выражениями, разместив их объявления непосредственно в обращениях к методу Array.SortQ.

Приведем программу, в которой элементы целочисленного массива упорядочиваются по убыванию значений, а затем сортируются по их четности. Для задания правил упорядочения применим в обращениях к методу Array. SortQ в качестве аргументов лямбда-выражения:

// 1706.cs — лямбда-выражения и Array. Sort () using System; class Program {.

static void Main ().

{

int[ ] ar = { 4, 5, 2, 7, 8, 1, 9, 3 >;

Array.Sort (ar, (int x, int у) => // no убыванию

{

if (x < y) return 1; if (x == y) return 0; return -1;

}

);

foreach (int memb in ar).

Console.Write («{0} «, memb);

Console.WriteLine ();

Array.Sort (ar, (int x, int у) => // no четности

{

if (x % 2 ≠ 0 & у % 2 == 0) return 1; if (x == y) return 0; return -1;

>

);

foreach (int memb in ar).

Console.Write («{0} «, memb);

} }

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

  • 98 754 321
  • 24 813 579

Так как каждый из методов используется в данной программе только один раз, то нет необходимости объявлять тип делегатов отдельно и определять ссылки с типом делегата. Лямбда-выражения представляют конкретные методы непосредственно в аргументах метода Array. SortQ.

Напомним, что делегат — это тип и поэтому может не только специфицировать параметры методов. В следующем примере делегат определяет тип свойства класса. В решаемой далее задаче нужно объявить делегат-тип для представления методов, вычисляющих значения логических функций двух переменных. Далее нужно определить класс, объект которого заполняет таблицу истинности для функции, представленной в объекте полем, имеющим тип делегата.

Для обращения к полю определить открытое свойство с типом делегата. (При заполнении таблицы истинности объект «не знает» для какой логической функции строится таблица; функция передается объекту в точке обращения к его методу.).

Итак, требуется определить класс со статическим методом для вывода на экран таблицы истинности логической функции двух переменных. При выводе заменять логическое значение ИСТИНА значением 1, а ЛОЖЬ — 0.

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

В методе Main () определить два лямбда-выражения, представляющие логические функции, и построить для них таблицы истинности.

// 1707.cs — свойство с типом делегата и анонимные методы

using System;

public delegate bool BoolDel (bool x, bool у); // Делегат-тип.

public class Create.

{ // Класс таблиц истинности

BoolDel specificFun; // поле, определяющее логическую функцию public BoolDel SpecificFun {.

set { specificFun = value; }.

}

public bool[,] defineQ { // Формирование логического массива bool[,] res = new bool[4, 3]; bool bx, by; int k = 0;

for (int i = 0; i <= 1; i++) for (int j = 0; j <= l; j++).

{

bx = (i == 0? false: true); by = (j == 0? false: true); res[k, 0] = bx; res[k, 1] = by;

res[k++, 2] = specificFun (bx, by);

>

return res;

>

}

public class Methods { // Класс с методами

static public void printTabl (bool[,] tabl).

{

Console.WriteLine («A В F»);

for (int i = 0; i < tabl. GetLength (0); i++).

Console.WriteLine («{0} {1} {2}» ,.

tabl[i, 0]? 1: 0, tabl[i, 1]? 1: 0, tabl[i, 2]? 1: 0);

} }

class Program.

{ // Основной класс программы static void Main ().

{

Create create = new CreateQ;

create.SpecificFun = (bool a, bool b) =>

{ return a || b; };

Console.WriteLine («Таблица для (A || B):»);

Methods. printTabl (create, define ()); create. SpecificFun = (bool d, bool e) =>

{ return d && !e; };

Console. 1л1г^е1_1пе («пТаблица для (A &&!B):»);

Methods.printTabl (create.define ());

}

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

Таблица для (А || В):

А В F 0 0 0 0 11 10 1 111.

Таблица для (А &&!В):

А В F 0 0 0 0 10 10 1 110.

Изучая лямбда-выражения и анонимные методы, следует обратить внимание на возможности применения в них внешних переменных. Как и для локальных методов, внешними считаются параметры и локальные переменные того метода, в котором определен анонимный метод или лямбда-выражение. Особенности использования внешних переменных в лямбда-выражениях, в локальных и анонимных методах совпадают, поэтому рассмотрим только лямбда-выражения.

Внешняя переменная, используемая в лямбда-выражении, называется захваченной переменной. Важно понимать, когда происходит «захват» значения, которое имеет захваченная переменная. Приведем пример:

// 17_Ь — захваченные переменные в лямбда-выражениях using System; class Program {.

delegate void Pusto (); static void Main ().

{

string name = «Tom» ;

Pusto output = () =>

{ Console. WriteLine ($" Hello, {name}!"); }; output (); name = «lack»; output ();

} }

Результаты, выводимые на экран:

Hello, Tom!

Hello, lack!

Делегат-тип Pusto представляет методы без параметров, ничего не возвращающие в точку вызова. Переменная output с типом этого делегата инициализирована с помощью лямбда-выражения, в теле которого используется внешняя переменная name. При обращениях к экземпляру делегата с помощью выражения output () используется то значение захваченной переменной пате, которое она имеет в момент обращения (а не в момент создания экземпляра делегата). Это правило иллюстрируют результаты выполнения программы.

Захваченная лямбда-выражением внешняя переменная продолжает существовать до тех пор, пока лямбда-выражение остается частью экземпляра делегата. Это происходит даже в том случае, когда переменная выходит из обычной области своего существования. Покажем эту особенность на примере:

// 17_с — расширение области существования переменных using System; class Program {.

delegate void Pusto ();

static void Main ().

{

Pusto output;

{

string report = «Сообщение из блока» ;

output = () => Console. WriteLine ($" {report}!");

}

outputQ;

} }

Результаты, выводимые на экран:

Сообщение из блока!

В методе Main () есть вложенный блок. Ссылка output определена вне этого блока, но ассоциирована с лямбда-выражением в теле внутреннего блока. В лямбда-выражение входит локальная переменная report внутреннего блока. Обращение к экземпляру делегата с помощью выражения output () выполнено за пределами внутреннего блока. Но используется значение захваченной переменной report из внутреннего блока, что иллюстрируют результаты выполнения программы.

Так как значение захваченной переменной определяется не в момент декларации лямбда-выражения, а в момент обращения к тому экземпляру делегата, в котором это выражение используется, то существует немного неожиданная ситуация, в том случае, когда лямбда-выражение захватывает параметр цикла.

Рассмотрим следующий фрагмент кода:

Pusto [] take = new Pusto[3];

for (char h = 'A'; h < 'D'; h++).

take[h — 'A'] = () => Console. Write (h + ««);

foreach (Pusto d in take) d ();

Декларирован массив, тип элементов которого определяет делегат Pusto. В параметрическом цикле каждый элемент массива инициализирован лямбда-выражением, которое захватывает переменную h — параметр цикла. После выхода из цикла for в цикле foreach выполняются обращения к элементам массива, каждый из которых выводит значение захваченного параметра. Так как обращения к элементам массива (к экземплярам делегата Pusto) выполняются после завершения параметрического цикла, то параметр цикла имеет то значение 'D', при котором цикл завершен. Результат на экране дисплея:

D D D.

Лямбда-выражения могут изменять захваченные переменные практически так же, как могут изменять операторы блока внешние по отношению к блоку переменные.

Показать весь текст
Заполнить форму текущей работой