Из Википедии, бесплатной энциклопедии
Перейти к навигации Перейти к поиску
Обратный вызов часто возвращается на уровень исходного вызывающего абонента.

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

Дизайн [ править ]

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

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

Реализация [ править ]

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

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

Используйте [ редактировать ]

C [ править ]

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

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

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

Пример вывода:

30920 и 315439161 и 924842 и 42

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

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

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

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

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

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

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

В C ++ функтор также широко используется рядом с использованием указателя функции в C.

C # [ править ]

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

открытый  класс  Class1  {  static  void  Main ( string []  args )  {  Class2  c2  =  new  Class2 ();  / *  * Вызов метода в Class2 с методом обратного вызова в качестве параметра  * /  c2 . Метод ( CallBackMethod );  } / *  * Метод обратного вызова. Этот метод печатает строку, отправленную в обратном вызове  * /  static  void  CallBackMethod ( string  str )  {  Console . WriteLine ( $ "Обратный вызов был: {str}" );  } }public  class  Class2 {  / *  * Метод, который обращается к вызывающей стороне. Принимает действие (метод) в качестве параметра  * /  public  void  Method ( Action < string >  callback )  {  / *  * Обратный вызов методу CallBackMet в Class1 с указанным сообщением  * /  callback ( «Сообщение для отправки обратно» );  } }

JavaScript [ править ]

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

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

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

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

Red and REBOL [ править ]

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

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

Lua [ править ]

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

ждать ( 1 ) локальный  DT  =  ждать ()функция  tween_color ( object ,  finish_color ,  fade_time )  локальный  step_r  =  finish_color . г  -  объект . BackgroundColor3 . r  локальный  step_g  =  finish_color . г  -  объект . BackgroundColor3 . g  локальный  step_b  =  finish_color . б  -  объект . BackgroundColor3 . b  локальный  total_steps  = 1 / ( DT * ( 1 / fade_time ))  локально  завершено ;  coroutine.wrap ( function ()  для  i  =  0 ,  1 ,  DT * ( 1  /  fade_time )  do  object . BackgroundColor3  =  Color3 . new  (  object . BackgroundColor3 . r  +  ( step_r / total_steps ),  object. BackgroundColor3 . g  +  ( step_g / total_steps ),  объект . BackgroundColor3 . b  +  ( step_b / total_steps )  )  wait ()  end,  если  завершено,  то  завершено ()  end  end ) ()  return  {  done  =  function ( callback )  completed  =  callback  end  } endtween_color ( некоторый_объект ,  Color3 . новый ( 1 ,  0 ,  0 ),  1 ). done ( function ()  print  "Анимация цвета завершена!" конец )

Python [ править ]

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

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

>>> def  get_square ( val ): ...  "" "Обратный вызов." "" ...  return  val  **  2 ... >>> def  caller ( func ,  val ): ...  return  func ( val ) ... >>> вызывающий абонент ( get_square ,  5 ) 25

См. Также [ править ]

  • Шаблон команды
  • Продолжение прохождения стиля
  • Цикл событий
  • Событийно-ориентированное программирование
  • Неявный вызов
  • Инверсия контроля
  • libsigc ++ , библиотека обратного вызова для C ++
  • Сигналы и слоты
  • Выход пользователя

Ссылки [ править ]

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

Внешние ссылки [ править ]

  • Основные инстинкты: реализация уведомлений об обратном вызове с помощью делегатов
  • Реализуйте процедуры обратного вызова в Java
  • Реализация инфраструктуры обратного вызова сценария в ASP.NET
  • Взаимодействие функций-членов C ++ с библиотеками C (архивировано 6 июля 2011 г.)
  • Пример использования стиля # 2: стандартные обратные вызовы