Обратный звонок (компьютерное программирование) - Callback (computer programming)

Обратный вызов часто возвращается на уровень исходного вызывающего.

В компьютерное программирование, а Перезвони, также известный как "call-after"[1] функция, любой исполняемый код который передается как аргумент на другой код; ожидается, что другой код Перезвони (выполнить) аргумент в заданное время. Это выполнение может быть немедленным, как в синхронный обратный вызов, или это может произойти позже, как в асинхронный обратный вызов. Языки программирования поддерживают обратные вызовы по-разному, часто реализуя их с подпрограммы, лямбда-выражения, блоки, или указатели на функции.

дизайн

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

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

Реализация

Форма обратного вызова зависит от языки программирования:

  • В сборка, C, C ++, Паскаль, Modula2 и подобные языки, машинный уровень указатель в функцию может быть передан как аргумент другой функции (внутренней или внешней). Это поддерживается большинством компиляторов и дает преимущество совместного использования разных языков без специальных библиотек-оболочек или классов. Одним из примеров может быть Windows API который напрямую (более или менее) доступен для многих различных языков, компиляторов и ассемблеров.
  • C ++ позволяет объектам предоставлять свою собственную реализацию операции вызова функции. В Стандартная библиотека шаблонов принимает эти объекты (называемые функторы ), а также указатели на функции в качестве параметров различных полиморфных алгоритмов.
  • Много динамические языки, такие как JavaScript, Lua, Python, Perl[2][3] и PHP, просто разрешите передачу функционального объекта.
  • Языки интерфейса командной строки такие как C # и VB.NET обеспечить типобезопасный инкапсулирующая ссылка, "делегировать ", чтобы определить хорошо напечатанный указатели на функции. Их можно использовать как обратные вызовы.
  • События и обработчики событий, используемые в языках .NET, предоставляют обобщенный синтаксис для обратных вызовов.
  • Функциональные языки обычно поддерживают первоклассные функции, которые могут быть переданы как обратные вызовы другим функциям, сохранены как данные или возвращены функциями.
  • Некоторые языки, например Алгол 68, Perl, Python, Рубин, Болтовня, C ++ 11 и более поздние версии более новых версий C # и VB.NET, а также большинства функциональных языков допускают безымянные блоки кода (лямбда-выражения ) вместо ссылок на функции, определенные в другом месте.
  • На некоторых языках, например Схема, ML, JavaScript, Perl, Python, Smalltalk, PHP (начиная с версии 5.3.0),[4] C ++ 11 и новее, Java (начиная с 8),[5] и многие другие, такие функции могут быть закрытие, т.е. они могут получать доступ и изменять переменные, локально определенные в контексте, в котором была определена функция. Обратите внимание, что Java не может, однако, изменять локальные переменные в охватывающей области.
  • В объектно-ориентированного программирования языки без аргументов со значением функции, такие как в Ява до его версии 8 обратные вызовы можно было моделировать, передавая экземпляр абстрактного класса или интерфейса, из которых получатель будет вызывать один или несколько методов, а вызывающая сторона предоставляет конкретную реализацию. Такие объекты фактически представляют собой набор обратных вызовов, а также данные, которые им необходимы для управления.[требуется разъяснение ]. Они полезны при реализации различных шаблоны проектирования такие как Посетитель, Наблюдатель, и Стратегия.

Использовать

C

Обратные вызовы имеют множество применений, например, для сигнализации об ошибках: Unix программа может не захотеть немедленно завершать работу, когда получит SIGTERM, поэтому, чтобы убедиться, что его завершение обрабатывается правильно, он должен зарегистрировать функцию очистки как обратный вызов. Обратные вызовы также могут использоваться для контроля того, действует функция или нет: Xlib позволяет указывать настраиваемые предикаты, чтобы определить, хочет ли программа обрабатывать событие.

Следующее C код демонстрирует использование обратных вызовов для отображения двух чисел.

#включают <stdio.h>#включают <stdlib.h>/ * Вызывающая функция принимает в качестве параметра единственный обратный вызов. * /пустота PrintTwoNumbers(int (*numberSource)(пустота)) {    int val1 = numberSource();    int val2 = numberSource();    printf("% d и% d п", val1, val2);}/ * Возможный обратный вызов * /int более девяти тысяч(пустота) {    вернуть (ранд()%1000) + 9001;}/ * Другой возможный обратный вызов. * /int Смысл жизни(пустота) {    вернуть 42;}/ * Здесь мы вызываем PrintTwoNumbers () с тремя разными обратными вызовами. * /int основной(пустота) {    time_t т;    srand((беззнаковый)время(&т)); // Инициируем семя для случайной функции    PrintTwoNumbers(&ранд);    PrintTwoNumbers(&более девяти тысяч);    PrintTwoNumbers(&Смысл жизни);    вернуть 0;}

Результат должен быть похож на:

125185 и 89187225 9084 и 9441 42 и 42

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

Другое преимущество заключается в том, что вызывающая функция может передавать любые параметры вызываемым функциям (не показаны в приведенном выше примере). Это позволяет правильно скрытие информации: код, который передает обратный вызов вызывающей функции, не должен знать значения параметров, которые будут переданы функции. Если бы он передавал только возвращаемое значение, тогда параметры должны были бы быть опубликованы.[пример необходим ]

Другой пример:

/* * Это простая программа на C, демонстрирующая использование обратных вызовов. * Функция обратного вызова находится в том же файле, что и вызывающий код. * Функция обратного вызова позже может быть помещена во внешнюю библиотеку, например * например, общий объект для увеличения гибкости. * */#включают <stdio.h>#включают <string.h>typedef структура _MyMsg {    int appId;    char msgbody[32];} MyMsg;пустота myfunc(MyMsg *сообщение){    если (Strlen(сообщение->msgbody) > 0 )        printf("Идентификатор приложения =% d  пMsg =% s  п",сообщение->appId, сообщение->msgbody);    еще        printf("Идентификатор приложения =% d  пMsg = Нет Msg п",сообщение->appId);}/* * Объявление прототипа */пустота (*Перезвони)(MyMsg *);int основной(пустота){    MyMsg msg1;    msg1.appId = 100;    strcpy(msg1.msgbody, "Это проверка п");    /*     * Назначьте адрес функции "myfunc" функции     * указатель "callback" (также может быть записан как "callback = & myfunc;")     */    Перезвони = myfunc;    /*     * Вызов функции (также может быть записано как "(* обратный вызов) (& msg1);")     */    Перезвони(&msg1);    вернуть 0;}

Результат после компиляции:

$ gcc cbtest.c$ ./a.outИдентификатор приложения = 100Msg = Это тест

Это сокрытие информации означает, что обратные вызовы могут использоваться при обмене данными между процессами или потоками или посредством сериализованных коммуникаций и табличных данных.[требуется разъяснение ]

C #

Простой обратный вызов в C #:

общественный класс Класс1 {    статический пустота Основной(строка[] аргументы)    {        Класс2 c2 = новый Класс2();                /*        * Метод вызова на Class2 с методом обратного вызова в качестве параметра        */        c2.Метод(CallBackMethod);    }    /*    * Метод обратного вызова. Этот метод печатает строку, отправленную в обратном вызове    */    статический пустота CallBackMethod(строка ул)    {        Консоль.WriteLine($"Обратный вызов был: {str}");    }}общественный класс Класс2{    /*    * Метод, который обращается к вызывающему абоненту. Принимает действие (метод) как параметр    */    общественный пустота Метод(Действие<строка> Перезвони)    {        /*        * Обратный вызов метода CallBackMet в Class1 с указанным сообщением        */        Перезвони("Сообщение для отправки");    }}

JavaScript

Обратные вызовы используются при реализации таких языков, как JavaScript, включая поддержку функций JavaScript в качестве обратных вызовов через js-ctypes[6] и в таких компонентах, как addEventListener.[7] Однако собственный пример обратного вызова может быть написан без какого-либо сложного кода:

функция вычислить(число1, число2, callbackFunction) {    вернуть callbackFunction(число1, число2);}функция calcProduct(число1, число2) {    вернуть число1 * число2;}функция calcSum(число1, число2) {    вернуть число1 + число2;}// предупреждение 75, произведение 5 и 15предупреждение(вычислить(5, 15, calcProduct));// предупреждает 20, сумма 5 и 15предупреждение(вычислить(5, 15, calcSum));

Сначала функция вычислить определяется параметром, предназначенным для обратного вызова: callbackFunction. Затем функция, которую можно использовать как обратный вызов для вычислить определено, calcProduct. Другие функции могут использоваться для callbackFunction, любить calcSum. В этом примере вычислить () вызывается дважды, один раз с calcProduct как обратный вызов и один раз с calcSum. Функции возвращают произведение и сумму соответственно, а затем предупреждение выводит их на экран.

В этом примитивном примере использование обратного вызова - это прежде всего демонстрация принципа. Можно просто вызвать обратные вызовы как обычные функции, calcProduct (число1, число2). Обратные вызовы обычно используются, когда функции необходимо выполнять события перед выполнением обратного вызова, или когда функция не имеет (или не может) иметь значимые возвращаемые значения для действий, как в случае с Асинхронный JavaScript (на основе таймеров) или XMLHttpRequest Запросы. Полезные примеры можно найти в Библиотеки JavaScript такие как jQuery где метод .each () выполняет итерацию по объекту, подобному массиву, причем первым аргументом является обратный вызов, который выполняется на каждой итерации.

Красный и REBOL

От JavaScript выше, вот как можно реализовать то же самое в любом REBOL или Красный (язык программирования). Обратите внимание на более четкое представление данных в виде кода.

  • подразумевается возврат, поскольку код в каждой функции является последней строкой блока
  • Поскольку для предупреждения требуется строка, форма создает строку из результата вычисления.
  • Главное слово! значения (например,: calc-product и: calc-sum) запускают интерпретатор для возврата кода функции, а не для оценки с помощью функции.
  • Тип данных! ссылки в блоке! [плавать! целое число!] ограничивают тип значений, передаваемых в качестве аргументов.
Красный [Заглавие: «Пример обратного звонка»]рассчитать: func [    число1 [количество!]     число2 [количество!]     callback-функция [функция!]][    callback-функция число1 число2]кальк-продукт: func [    число1 [количество!]     число2 [количество!]][    число1 * число2]расчетная сумма: func [    число1 [количество!]     число2 [количество!]][    число1 + число2]; оповещения 75, произведение 5 и 15предупреждение форма вычислить 5 15 : calc-product; предупреждений 20, сумма 5 и 15предупреждение форма вычислить 5 15 : calc-sum

Lua

Пример анимации цвета с использованием Роблокс движок, который принимает необязательный обратный вызов .done:

Подождите(1)местный DT = Подождите()функция tween_color(объект, finish_color, fade_time)  местный step_r = finish_color.р - объект.ФонЦвет3.р  местный step_g = finish_color.г - объект.ФонЦвет3.г  местный step_b = finish_color.б - объект.ФонЦвет3.б  местный total_steps = 1/(DT*(1/fade_time))  местный завершено;  coroutine.wrap(функция()    для я = 0, 1, DT*(1 / fade_time) делать      объект.ФонЦвет3 = Цвет3.новый (        объект.ФонЦвет3.р + (step_r/total_steps),        объект.ФонЦвет3.г + (step_g/total_steps),        объект.ФонЦвет3.б + (step_b/total_steps)      )      Подождите()    конец    если завершено тогда      завершено()    конец  конец)()  вернуть {    сделанный = функция(Перезвони)      завершено = Перезвони    конец  }конецtween_color(some_object, Цвет3.новый(1, 0, 0), 1).сделанный(функция()  Распечатать "Анимация цвета завершена!"конец)

Python

Классическое использование обратных вызовов в Python (и других языках) - назначать события элементам пользовательского интерфейса.

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

>>> def get_square(вал):...     "" "Обратный вызов." ""...     вернуть вал ** 2...>>> def звонящий(func, вал):...     вернуть func(вал)...>>> звонящий(get_square, 5)25

Смотрите также

использованная литература

  1. ^ "Что такое функция обратного вызова?". Переполнение стека. Получено 2018-05-16.
  2. ^ «Поваренная книга Perl - 11.4. Ссылки на функции». Получено 2008-03-03.
  3. ^ «Расширенное программирование на Perl - 4.2 Использование ссылок на подпрограммы». Получено 2008-03-03.
  4. ^ «Справочник по языку PHP - Анонимные функции». Получено 2011-06-08.
  5. ^ «Что нового в JDK 8». oracle.com.
  6. ^ «Обратные звонки». Сеть разработчиков Mozilla. Получено 13 декабря 2012.
  7. ^ «Создание обратных вызовов Javascript в компонентах». Сеть разработчиков Mozilla. Получено 13 декабря 2012.

внешние ссылки