Делегаты и обратные вызовы
При разработке средств обратного вызова в языке C# было решено обеспечить программистов не только возможностью контролировать сигнатуру методов, но и отличать методы классов от методов объектов. Каждый делегат включает в качестве полей ссылку на объект, для которого нужно вызвать метод, и имя конкретного метода. Если ссылка на объект равна null, то имя метода воспринимается как имя статического… Читать ещё >
Делегаты и обратные вызовы (реферат, курсовая, диплом, контрольная)
В программировании достаточно часто возникает необходимость создавать фрагменты кода (например, классы, подпрограммы, функции), которые можно было бы каким-либо образом настраивать при конкретных применениях. Наиболее интересна и важна настройка, позволяющая изменять алгоритм выполнения кода. Классическим примером удобства и полезности такой настройки служат методы Sort () и BinarySearchO класса Array. Первый из них выполняет сортировку элементов массива, второй позволяет осуществлять бинарный поиск нужного значения в упорядоченном массиве. Применение каждого из названных методов требует, чтобы программист-пользователь «сообщил» при вызове, что он понимает под порядком, в котором должны разместиться сортируемые элементы, или что означает равенство значений элемента массива и эталона поиска. Указанные критерии сортировки и поиска оформляются в виде вспомогательных методов. Сведения об этих методах передаются методам Sort () и BinarySearchO в качестве аргументов вместе с тем массивом, который нужно обработать. Благодаря этому в программе пользователя появляется возможность, по-разному программируя вспомогательные методы, достаточно произвольно задавать правила сортировки и поиска, т. е. изменять поведение библиотечных методов Sort () и BinarySearchO.
О концепции построения методов, вызывающих при исполнении вспомогательные функции, определенные (позже) в точке вызова (например, в коде пользователя), говорят, используя термин «обратный вызов» (callback). В примере с Sort () и BinarySearchO методы обратного вызова параметризуют названные библиотечные методы.
Еще один пример применения методов обратного вызова — процедура вывода на экран дисплея графика функции Дх), математическое описание которой заранее не известно. При обращении к такой процедуре могут быть заданы (например) пределы изменения аргумента xmin, xmax и ссылка на программную реализацию конкретной функции Дх).
Третий пример — процедура вычисления определенного интеграла функции Дх). Так как существует много методов численного интегрирования, то при использовании процедуры можно с помощью параметров задать: программную реализацию метода интегрирования, реализацию (метод) подинтегральной функцииДх), пределы интегрирования и требуемую точность вычислений. В данном случае процедура вычисления определенного интеграла параметризуется двумя методами обратного вызова.
Итак, обратным вызовом называют обращение из исполняемого метода к другому методу, который зачастую определяется не на этапе компиляции, а в процессе выполнения программы. В языках С и C + + для реализации обратных вызовов в качестве параметра в процедуру передается указатель на ту функцию, к которой нужно обращаться при исполнении программы. Мы не рассматриваем механизм указателей, но заметим, что указатель на функцию — это адрес участка основной памяти, в котором размещен код функции. Опасность и ненадежность обращения к функции по ее адресу состоит в том, что зная только адрес, невозможно проверить правильность обращения. Ведь нужно убедиться, что количество аргументов и их типы соответствуют параметрам функции, что верен тип возвращаемого значения и т. д.
Возможность указанных проверок в языке C# обеспечивают делегаты.
При разработке средств обратного вызова в языке C# было решено обеспечить программистов не только возможностью контролировать сигнатуру методов, но и отличать методы классов от методов объектов. Каждый делегат включает в качестве полей ссылку на объект, для которого нужно вызвать метод, и имя конкретного метода. Если ссылка на объект равна null, то имя метода воспринимается как имя статического метода.
Как мы уже говорили, делегат — это тип, экземпляры которого представляют методы с конкретной спецификацией параметров и фиксированным типом возвращаемого значения. Делегаты дают возможность рассматривать методы как сущности, ссылки на которые можно присваивать переменным (соответствующего типа) и передавать в качестве параметров.
Покажем на простом примере, как ссылка на экземпляр делегата может использоваться в качестве параметра для организации обратных вызовов.
Определим класс, поля которого задают предельные значения xmin, xmax отрезка числовой оси и количество п равноотстоящих точек на этом отрезке. В классе определим метод, выводящий на экран значения функции /(х) в точках числовой оси, определенных значениями Хщщ, *тах' п• Конкретная функция/(х), передается методу с помощью параметра-делегата. Определения делегата-типа и класса:
public delegate double Pnoc (double x); // делегат-тип
public class Series.
{
int n;
double xmi, xma;
public Series (int ni, double xn, double xk) 11 конструктор { n = ni; xmi = xn; xma = xk; } public void display (Proc fun).
{
Console.WriteLine («Proc: {0}, xmi={l:f2}, «+.
" xma={2:f2}, n={3}.", fun. Method, xmi, xma, n); for (double x = xmi; x <= xma; x += (xma — xmi) / (n — 1)) Console. Write («{0:f2} «, fun (x));
} }
Делегат-тип Proc определен как внешний тип в глобальном пространстве имен. Там же определен класс Series. Для нас интересен его метод с заголовком.
public void display (Proc fun).
Параметр метода — ссылка на экземпляр делегата Proc. В теле метода displayO выводится заголовок того метода, который будет представлен аргументом-делегатом. Затем выводятся п значений адресованного аргументом метода, представляющего функцию Дх). Значения выводятся в цикле, параметром которого служит аргумент функции Дх).
Имея такие определения типа-делегата и класса, можно вводить разные методы, соответствующие делегату Proc, и определять разные объекты класса Series. Затем для объекта класса Series можно вызвать метод displayO, передать ему в качестве аргумента имя метода, представляющего нужную математическую функцию Дх), и получить заголовок метода, представляющего функцию Дх), и набор значений этой функции. Указанные действия выполняет следующий фрагмент программы:
class Program.
{
static double mySin (double x) // Математическая функция { return Math. Sin (x); }.
static double myLine (double x) // Математическая функция { return x * 5; } static void Main ().
{
Series sequence = new Series (7, 0.0, 1.0); // Объект класса sequence. display (mySin);
Console.WriteLine (); sequence. display (myLine);
Console.WriteLine ();
}
>
В классе Program определены два статических метода, соответствующих делегату-типу Proc. В методе Main () определен именуемый ссылкой sequence объект класса Series. Поля этого объекта (xmin, xmax, n) определяют набор точек оси аргумента, в которых будут вычисляться значения функции при обращении к методу displayO.
Результат выполнения программы:
Ргос: Double mySin (Double), xmi=0,00, xma=l, 00, n=7.
0,00 0,17 0,33 0,48 0,62 0,74 0,84.
Ргос: Double myLine (Double), xmi=0,00, xma=l, 00, n=7.
0,00 0,83 1,67 2,50 3,33 4,17 5,00.
Обратите внимание, что в данном примере нет явного создания экземпляра делегата Ргос. Нет явного определения и ссылок на экземпляры делегатов. Вместо этого при обращении к методу displayO параметр fun заменяется именем конкретного метода. Все дальнейшие действия выполняются неявно — создается экземпляр делегата-типа Ргос и ссылка на него используется в теле метода.