Oddzwanianie (programowanie komputerowe) - Callback (computer programming)

Oddzwanianie jest często z powrotem na poziomie pierwotnego rozmówcy.

W programowaniu komputerowym , a zwrotnego , znany również jako „ call-pofunkcji , jest dowolny kod wykonywalny , który jest przekazywany jako argumentu do innego kodu; oczekuje się, że inny kod oddzwoni (wykona) argument w określonym czasie. To wykonanie może być natychmiastowe, jak w przypadku synchronicznego wywołania zwrotnego , lub może nastąpić później, jak w przypadku asynchronicznego wywołania zwrotnego . Języki programowania obsługują wywołania zwrotne na różne sposoby, często implementując je za pomocą podprogramów , wyrażeń lambda , bloków lub wskaźników funkcji .

Projekt

Istnieją dwa typy wywołań zwrotnych, różniące się sposobem sterowania przepływem danych w czasie wykonywania: wywołania zwrotne blokujące (znane również jako wywołania zwrotne synchroniczne lub po prostu wywołania zwrotne ) oraz wywołania zwrotne odroczone (znane również jako wywołania zwrotne asynchroniczne ). Podczas gdy wywołania zwrotne blokujące są wywoływane przed zwróceniem funkcji (w poniższym przykładzie w języku C, który ilustruje wywołanie zwrotne blokujące, jest to funkcja main ), wywołania zwrotne mogą być wywoływane po zwróceniu funkcji. Odroczone wywołania zwrotne są często używane w kontekście operacji we/wy lub obsługi zdarzeń i są wywoływane przez przerwania lub przez inny wątek w przypadku wielu wątków. Ze względu na swój charakter wywołania zwrotne blokujące mogą działać bez przerwań lub wielu wątków, co oznacza, że ​​wywołania zwrotne nie są powszechnie używane do synchronizacji lub delegowania pracy do innego wątku.

Wywołania zwrotne są używane do programowania aplikacji w systemach okienkowych . W takim przypadku aplikacja dostarcza (odwołanie do) określonej niestandardowej funkcji wywołania zwrotnego do wywołania przez system operacyjny, która następnie wywołuje tę funkcję specyficzną dla aplikacji w odpowiedzi na zdarzenia, takie jak kliknięcia myszą lub naciśnięcia klawiszy. Głównym problemem jest tutaj zarządzanie uprawnieniami i bezpieczeństwem: podczas gdy funkcja jest wywoływana z systemu operacyjnego, nie powinna działać z tymi samymi uprawnieniami, co system. Rozwiązaniem tego problemu jest zastosowanie pierścieni ochronnych.

Realizacja

Forma wywołania zwrotnego różni się w zależności od języka programowania :

  • W asemblerze , C , C++ , Pascalu , Modula2 i podobnych językach wskaźnik na poziomie maszyny do funkcji może być przekazany jako argument do innej (wewnętrznej lub zewnętrznej) funkcji. Jest to obsługiwane przez większość kompilatorów i zapewnia zaletę jednoczesnego używania różnych języków bez specjalnych bibliotek lub klas opakowujących. Jednym z przykładów może być Windows API, który jest bezpośrednio (mniej lub bardziej) dostępny dla wielu różnych języków, kompilatorów i asemblerów.
  • C++ umożliwia obiektom zapewnienie własnej implementacji operacji wywołania funkcji. Standard Template Biblioteka przyjmuje te przedmioty (zwane Funktory ), jak również wskaźniki funkcyjnych, jako parametry do różnych algorytmów polimorficznych.
  • Wiele języków dynamicznych , takich jak JavaScript , Lua , Python , Perl i PHP , po prostu umożliwia przekazywanie obiektu funkcji.
  • Języki CLI, takie jak C# i VB.NET, zapewniają bezpieczne dla typów odwołanie do hermetyzacji, " delegat ", aby zdefiniować dobrze wpisane wskaźniki funkcji . Mogą być używane jako wywołania zwrotne.
  • Zdarzenia i programy obsługi zdarzeń , używane w językach .NET, zapewniają uogólnioną składnię wywołań zwrotnych.
  • Języki funkcjonalne zazwyczaj obsługują funkcje pierwszej klasy , które mogą być przekazywane jako wywołania zwrotne do innych funkcji, przechowywane jako dane lub zwracane z funkcji.
  • Niektóre języki, takie jak Algol 68 , Perl, Python, Ruby , Smalltalk , C++11 i nowsze, nowsze wersje C# i VB.NET, a także większość języków funkcjonalnych, pozwalają na dostarczanie nienazwanych bloków kodu ( wyrażeń lambda ) zamiast odwołań do funkcji zdefiniowanych w innym miejscu.
  • W niektórych językach, np Scheme , ML , JavaScript, Perl, Python, Smalltalk, PHP (od 5.3.0), C ++ 11 i później, Java (od 8) i wielu innych, te funkcje mogą być zamknięcia , tj może uzyskiwać dostęp i modyfikować zmienne lokalnie zdefiniowane w kontekście, w którym funkcja została zdefiniowana. Należy zauważyć, że Java nie może jednak modyfikować zmiennych lokalnych w otaczającym zasięgu.
  • W obiektowych językach programowania bez argumentów o wartościach funkcji, takich jak Java przed jej wersją 8, wywołania zwrotne mogą być symulowane przez przekazanie instancji klasy abstrakcyjnej lub interfejsu, których odbiorca wywoła jedną lub więcej metod, podczas gdy koniec zapewnia konkretną realizację. Takie obiekty są w rzeczywistości pakietem wywołań zwrotnych oraz danymi, których potrzebują do manipulowania. Są przydatne przy wdrażaniu różnych wzorców projektowych, takich jak Odwiedzający , Obserwator i Strategia .

Posługiwać się

do

Wywołania zwrotne mają wiele zastosowań, na przykład w sygnalizacji błędów: program uniksowy może nie chcieć zakończyć natychmiast po otrzymaniu SIGTERM , więc aby upewnić się, że jego zakończenie jest obsługiwane prawidłowo, zarejestrowałby funkcję czyszczenia jako wywołanie zwrotne. Wywołania zwrotne mogą być również używane do kontrolowania, czy funkcja działa, czy nie: Xlib umożliwia określenie niestandardowych predykatów w celu określenia, czy program chce obsłużyć zdarzenie.

Poniższy kod C demonstruje użycie wywołań zwrotnych do wyświetlania dwóch liczb.

#include <stdio.h>
#include <stdlib.h>

/* The calling function takes a single callback as a parameter. */
void PrintTwoNumbers(int (*numberSource)(void)) {
    int val1 = numberSource();
    int val2 = numberSource();
    printf("%d and %d\n", val1, val2);
}

/* A possible callback */
int overNineThousand(void) {
    return (rand()%1000) + 9001;
}

/* Another possible callback. */
int meaningOfLife(void) {
    return 42;
}

/* Here we call PrintTwoNumbers() with three different callbacks. */
int main(void) {
    time_t t;
    srand((unsigned)time(&t)); // Init seed for random function
    PrintTwoNumbers(&rand);
    PrintTwoNumbers(&overNineThousand);
    PrintTwoNumbers(&meaningOfLife);
    return 0;
}

Przykładowe dane wyjściowe:

25926 and 14941
9015 and 9630
42 and 42

Zauważ, że różni się to od zwykłego przekazywania wyjścia funkcji zwrotnej do funkcji wywołującej PrintTwoNumbers() - zamiast drukowania tej samej wartości dwukrotnie, funkcja PrintTwoNumbers wywołuje funkcję zwrotną tyle razy, ile jest to wymagane. To jedna z dwóch głównych zalet wywołań zwrotnych.

Inną zaletą jest to, że funkcja wywołująca może przekazać dowolne parametry do wywoływanych funkcji (nie pokazano w powyższym przykładzie). Pozwala to na poprawne ukrywanie informacji : kod, który przekazuje wywołanie zwrotne do funkcji wywołującej, nie musi znać wartości parametrów, które zostaną przekazane do funkcji. Gdyby przekazał tylko wartość zwracaną, parametry musiałyby zostać ujawnione publicznie.

Inny przykład:

/*
 * This is a simple C program to demonstrate the usage of callbacks
 * The callback function is in the same file as the calling code.
 * The callback function can later be put into external library like
 * e.g. a shared object to increase flexibility.
 *
 */

#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("App Id = %d \nMsg = %s \n",msg->appId, msg->msgbody);
    else
        printf("App Id = %d \nMsg = No Msg\n",msg->appId);
}

/*
 * Prototype declaration
 */
void (*callback)(MyMsg *);

int main(void)
{
    MyMsg msg1;
    msg1.appId = 100;
    strcpy(msg1.msgbody, "This is a test\n");

    /*
     * Assign the address of the function "myfunc" to the function
     * pointer "callback" (may be also written as "callback = &myfunc;")
     */
    callback = myfunc;

    /*
     * Call the function (may be also written as "(*callback)(&msg1);")
     */
    callback(&msg1);

    return 0;
}

Dane wyjściowe po kompilacji:

$ gcc cbtest.c
$ ./a.out
App Id = 100
Msg = This is a test

To ukrywanie informacji oznacza, że ​​wywołania zwrotne mogą być używane podczas komunikacji między procesami lub wątkami albo za pośrednictwem komunikacji serializowanej i danych tabelarycznych.

W C++ funktor jest również powszechnie używany oprócz użycia wskaźnika funkcji w C.

DO#

Proste wywołanie zwrotne w C# :

public class Class1 
{
    static void Main(string[] args)
    {
        Class2 c2 = new Class2();
        
        /*
        * Calling method on Class2 with callback method as parameter
        */
        c2.Method(CallBackMethod);
    }

    /*
    * The callback method. This method prints the string sent in the callback
    */
    static void CallBackMethod(string str)
    {
        Console.WriteLine($"Callback was: {str}");
    }
}

public class Class2
{
    /*
    * The method that calls back to the caller. Takes an action (method) as parameter
    */
    public void Method(Action<string> callback)
    {
        /*
        * Calls back to method CallBackMet in Class1 with the message specified
        */
        callback("The message to send back");
    }
}

JavaScript

Wywołania zwrotne są używane w implementacji języków takich jak JavaScript , w tym w obsłudze funkcji JavaScript jako wywołań zwrotnych poprzez js-ctypes oraz w komponentach takich jak addEventListener. Jednak natywny przykład wywołania zwrotnego można napisać bez skomplikowanego kodu:

function calculate(num1, num2, callbackFunction) {
    return callbackFunction(num1, num2);
}

function calcProduct(num1, num2) {
    return num1 * num2;
}

function calcSum(num1, num2) {
    return num1 + num2;
}
// alerts 75, the product of 5 and 15
alert(calculate(5, 15, calcProduct));
// alerts 20, the sum of 5 and 15
alert(calculate(5, 15, calcSum));

Najpierw definiujemy funkcję oblicz z parametrem przeznaczonym do wywołania zwrotnego: callbackFunction . Następnie definiowana jest funkcja, która może być użyta jako wywołanie zwrotne do obliczenia , calcProduct . Inne funkcje mogą być użyte do funkcji callbackFunction , takie jak calcSum . W tym przykładzie metodacolc () jest wywoływana dwukrotnie, raz z funkcją calcProduct jako wywołaniem zwrotnym i raz z funkcją calcSum . Funkcje zwracają odpowiednio produkt i sumę, a następnie alert wyświetli je na ekranie.

W tym prymitywnym przykładzie użycie wywołania zwrotnego jest przede wszystkim demonstracją zasady. Można po prostu wywołać wywołania zwrotne jako zwykłe funkcje, calcProduct(num1, num2) . Wywołania zwrotne są zwykle używane, gdy funkcja musi wykonać zdarzenia przed wykonaniem wywołania zwrotnego lub gdy funkcja nie ma (lub nie może) mieć znaczących wartości zwracanych do działania, jak ma to miejsce w przypadku asynchronicznego JavaScript (opartego na licznikach) lub żądań XMLHttpRequest . Przydatne przykłady można znaleźć w bibliotekach JavaScript, takich jak jQuery, gdzie metoda .each() iteruje po obiekcie podobnym do tablicy, a pierwszym argumentem jest wywołanie zwrotne wykonywane w każdej iteracji.

Czerwony i REBOL

Z powyższego JavaScript , oto jak można zaimplementować to samo w REBOL lub Red (język programowania) . Zwróć uwagę na czystszą prezentację danych w postaci kodu.

  • return jest implikowane, ponieważ kod w każdej funkcji jest ostatnim wierszem bloku
  • Ponieważ alert wymaga ciągu, formularz tworzy ciąg z wyniku obliczenia
  • Słowo zdobądź! wartości (tj. :calc-product i :calc-sum) powodują, że interpreter zwraca kod funkcji zamiast oceniać ją za pomocą funkcji.
  • Typ danych! referencje w bloku! [pływak! liczba całkowita!] ograniczają typ wartości przekazywanych jako argumenty.
Red [Title: "Callback example"]

calculate: func [
    num1 [number!] 
    num2 [number!] 
    callback-function [function!]
][
    callback-function num1 num2
]

calc-product: func [
    num1 [number!] 
    num2 [number!]
][
    num1 * num2
]

calc-sum: func [
    num1 [number!] 
    num2 [number!]
][
    num1 + num2
]

; alerts 75, the product of 5 and 15
alert form calculate 5 15 :calc-product

; alerts 20, the sum of 5 and 15
alert form calculate 5 15 :calc-sum

Lua

Przykład animacji kolorów przy użyciu silnika Roblox , który pobiera opcjonalne wywołanie zwrotne .done:

wait(1)
local DT = wait()

function tween_color(object, finish_color, fade_time)
  local step_r = finish_color.r - object.BackgroundColor3.r
  local step_g = finish_color.g - object.BackgroundColor3.g
  local step_b = finish_color.b - object.BackgroundColor3.b
  local total_steps = 1/(DT*(1/fade_time))
  local completed;
  coroutine.wrap(function()
    for 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),
        object.BackgroundColor3.b + (step_b/total_steps)
      )
      wait()
    end
    if completed then
      completed()
    end
  end)()
  return {
    done = function(callback)
      completed = callback
    end
  }
end

tween_color(some_object, Color3.new(1, 0, 0), 1).done(function()
  print "Color tweening finished!"
end)

Pyton

Klasycznym zastosowaniem wywołań zwrotnych w Pythonie (i innych językach) jest przypisanie zdarzeń do elementów interfejsu użytkownika.

Oto bardzo trywialny przykład użycia wywołania zwrotnego w Pythonie. Najpierw zdefiniuj dwie funkcje, wywołanie zwrotne i kod wywołujący, a następnie przekaż funkcję wywołania zwrotnego do kodu wywołującego.

>>> def get_square(val):
...     """The callback."""
...     return val ** 2
...
>>> def caller(func, val):
...     return func(val)
...
>>> caller(get_square, 5)
25

Zobacz też

Bibliografia

Linki zewnętrzne