Callback (datorprogrammering)
I datorprogrammering är en callback- eller callback-funktion en referens till körbar kod som skickas som ett argument till en annan kodbit; den koden förväntas ringa tillbaka (köra) återuppringningsfunktionen som en del av sitt jobb. Denna exekvering kan vara omedelbar som vid en synkron återuppringning , eller så kan den ske vid en senare tidpunkt som vid en asynkron återuppringning . Programmeringsspråk stöder callbacks på olika sätt, och implementerar dem ofta med subrutiner , lambda-uttryck , block eller funktionspekare .
Design
Det finns två typer av återuppringningar, som skiljer sig åt i hur de styr dataflödet under körning: blockering av återuppringningar (även känd som synkrona återuppringningar eller bara återuppringningar ) och uppskjutna återuppringningar (även känd som asynkrona återuppringningar ). Medan blockerande återuppringningar anropas innan en funktion returnerar (som i C-exemplet nedan), kan uppskjutna återuppringningar anropas efter att en funktion återvänder. Uppskjutna återuppringningar används ofta i samband med I/O-operationer eller händelsehantering, och anropas av avbrott eller av en annan tråd i händelse av flera trådar. På grund av sin natur kan blockering av återuppringningar fungera utan avbrott eller flera trådar, vilket innebär att blockering av återuppringningar inte vanligtvis används för synkronisering eller för att delegera arbete till en annan tråd.
Callbacks används för att programmera applikationer i fönstersystem . I det här fallet tillhandahåller applikationen (en referens till) en specifik anpassad återuppringningsfunktion för operativsystemet att anropa, som sedan anropar denna applikationsspecifika funktion som svar på händelser som musklick eller tangenttryckningar. Ett stort problem här är hanteringen av privilegier och säkerhet: medan funktionen anropas från operativsystemet, bör den inte köras med samma privilegium som systemet. En lösning på detta problem är att använda skyddsringar .
Genomförande
Formen för en återuppringning varierar mellan programmeringsspråken :
- I assembly , C , C++ , Pascal , Modula2 och liknande språk kan en pekare på maskinnivå till en funktion skickas som ett argument till en annan (intern eller extern) funktion. Detta stöds av de flesta kompilatorer och ger fördelen av att använda olika språk tillsammans utan speciella omslagsbibliotek eller klasser. Ett exempel kan vara Windows API som är direkt (mer eller mindre) tillgängligt för många olika språk, kompilatorer och assemblerare.
- C++ tillåter objekt att tillhandahålla sin egen implementering av funktionsanropsoperationen. Standardmallbiblioteket accepterar dessa objekt (kallade functors ), såväl som funktionspekare, som parametrar till olika polymorfa algoritmer .
- Många dynamiska språk , som JavaScript , Lua , Python , Perl och PHP , tillåter helt enkelt att ett funktionsobjekt passeras igenom.
- CLI-språk som C# och VB.NET tillhandahåller en typsäker inkapslande referens, en " delegat ", för att definiera välskrivna funktionspekare . Dessa kan användas som återuppringningar.
- Händelser och händelsehanterare , som används i .NET-språk, tillhandahåller generaliserad syntax för återuppringningar.
- Funktionella språk stöder i allmänhet förstklassiga funktioner , som kan skickas som återuppringningar till andra funktioner, lagras som data eller returneras från funktioner.
- Vissa språk, såsom Algol 68 , Perl, Python, Ruby , Smalltalk , C++11 och senare, nyare versioner av C# och VB.NET samt de flesta funktionella språk, tillåter att icke namngivna kodblock ( lambda-uttryck ) tillhandahålls istället för referenser till funktioner definierade på annat håll.
- I vissa språk, t.ex. Scheme , ML , JavaScript, Perl, Python, Smalltalk, PHP (sedan 5.3.0), C++11 och senare, Java (sedan 8) och många andra, kan sådana funktioner vara stängningar , dvs. kan komma åt och ändra variabler som är lokalt definierade i det sammanhang där funktionen definierades. Observera att Java dock inte kan modifiera de lokala variablerna i det omslutande omfånget.
- I objektorienterade programmeringsspråk utan funktionsvärda argument, som i Java före dess 8-version, kan callbacks simuleras genom att skicka en instans av en abstrakt klass eller gränssnitt, varav mottagaren kommer att anropa en eller flera metoder, medan anropet slutet ger ett konkret genomförande. Sådana objekt är i själva verket en bunt av callbacks, plus den data de behöver för att manipulera [ förtydligande behövs ] . De är användbara för att implementera olika designmönster som besökare , observatör och strategi .
Använda sig av
C
Återuppringningar har många olika användningsområden, till exempel vid felsignalering: ett Unix -program kanske inte vill avslutas omedelbart när det tar emot SIGTERM , så för att säkerställa att dess avslutning hanteras korrekt, skulle det registrera rensningsfunktionen som ett återuppringning. Callbacks kan också användas för att kontrollera om en funktion fungerar eller inte: Xlib tillåter att anpassade predikat specificeras för att avgöra om ett program vill hantera en händelse.
Följande C -kod visar användningen av återuppringningar för att visa två nummer.
0
#include <stdio.h> #include <stdlib.h> #include <time.h> /* Den anropande funktionen tar en enda callback som parameter. */ void PrintTwoNumbers ( int ( * numberSource )( void )) { int val1 = numberSource (); int val2 = nummerKälla (); printf ( "%d och %d \n " , val1 , val2 ); } /* En möjlig återuppringning */ int overNineThousand ( void ) { return ( rand () % 1000 ) + 9001 ; } /* En annan möjlig återuppringning. */ int meaningOfLife ( void ) { return 42 ; } /* Här anropar vi PrintTwoNumbers() med tre olika callbacks. */ int main ( void ) { time_t t ; srand (( osignerad ) tid ( & t )); // Initiera frön för slumpmässig funktion PrintTwoNumbers ( & rand ); PrintTwoNumbers ( & overNineThousand ); PrintTwoNumbers ( & meaningOfLife ); återvända ; }
Exempel på utdata:
20671 och 25151 9800 och 9112 42 och 42
Lägg märke till hur detta skiljer sig från att bara skicka utdata från återuppringningsfunktionen till den anropande funktionen, PrintTwoNumbers() - istället för att skriva ut samma värde två gånger, anropar PrintTwoNumbers återuppringningen så många gånger som den kräver. Detta är en av de två främsta fördelarna med återuppringningar.
Den andra fördelen är att den anropande funktionen kan skicka vilka parametrar den vill till de anropade funktionerna (visas inte i exemplet ovan). Detta tillåter korrekt informationsdöljning : koden som skickar en återuppringning till en anropande funktion behöver inte känna till parametervärdena som kommer att skickas till funktionen. Om den bara passerade returvärdet skulle parametrarna behöva exponeras offentligt. [ exempel behövs ]
Ett annat exempel:
0
0
/* * Detta är ett enkelt C-program för att demonstrera användningen av återuppringningar * Återuppringningsfunktionen finns i samma fil som anropskoden. * Återuppringningsfunktionen kan senare läggas in i externt bibliotek som * t.ex. ett delat objekt för att öka flexibiliteten. * */ #include <stdio.h> #include <string.h> typedef struct _MyMsg { int appId ; char msgbody [ 32 ]; } Mitt meddelande ; void myfunc ( MyMsg * msg ) { if ( strlen ( msg -> msgbody ) > ) printf ( "App Id = %d \n Msg = %s \n " , msg -> appId , msg -> msgbody ); else printf ( "App Id = %d \n Msg = No Msg \n " , msg -> appId ); } /* * Prototypdeklaration */ void ( * callback )( MyMsg * ); int main ( void ) { MyMsg msg1 ; msg1 . appId = 100 ; strcpy ( msg1 . msgbody , "Detta är ett test \n " ); /* * Tilldela adressen för funktionen "myfunc" till funktionen * pointer "callback" (kan också skrivas som "callback = &myfunc;") */ callback = myfunc ; /* * Anropa funktionen (kan också skrivas som "(*callback)(&msg1);") */ callback ( & msg1 ); återvända ; }
Utdata efter kompilering:
$ gcc cbtest.c $ ./a.out App Id = 100 Msg = Detta är ett test
Denna informationsdöljning innebär att återuppringningar kan användas vid kommunikation mellan processer eller trådar, eller genom serialiserad kommunikation och tabelldata. [ förtydligande behövs ]
I C++ används functor också ofta vid sidan av användningen av funktionspekare i C.
C#
En enkel återuppringning i C# :
public class Class1 { static void Main ( sträng [] args ) { Class2 c2 = new Class2 (); /* * Anropsmetod på Class2 med callback-metod som parameter */ c2 . Metod ( CallBackMethod ); } /* * Återuppringningsmetoden. Denna metod skriver ut strängen som skickas i callback */ static void CallBackMethod ( string str ) { Console . WriteLine ( $"Återuppringning var: {str}" ); } } public class Class2 { /* * Metoden som ringer tillbaka till den som ringer. Utför en åtgärd (metod) som parameter */ public void Metod ( Action < string > callback ) { /* * Ringer tillbaka till metoden CallBackMet i Class1 med meddelandet specificerat */ callback ( "Meddelandet att skicka tillbaka" ); } }
Kotlin
En enkel återuppringning i Kotlin :
fun main (){ print ( "Ange text: " ) val question = readLine () answer ( question , :: meaningOfLife ) } fun meaningOfLife (): Int { return 42 } roligt svar ( fråga : String? , svar : () -> Int ) { println ( "Din fråga: $ fråga " ) println ( " Svar: ${ svar () } " ) }
JavaScript
Callbacks används i implementeringen av språk som JavaScript , inklusive stöd för JavaScript-funktioner som callbacks genom js-ctypes och i komponenter som addEventListener. Ett inbyggt exempel på en återuppringning kan dock skrivas utan någon komplex kod:
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, produkten av 5 och 15 alert ( beräkna ( 5 , 15 , calcProduct ) ); // varningar 20, summan av 5 och 15 alert ( beräkna ( 5 , 15 , calcSum ));
Först definieras en funktion beräkna med en parameter avsedd för callback: callbackFunction . Sedan definieras en funktion som kan användas som en callback för att beräkna , calcProduct . Andra funktioner kan användas för callbackFunction , som calcSum . I det här exemplet anropas calculate() två gånger, en gång med calcProduct som återuppringning och en gång med calcSum . Funktionerna returnerar produkten respektive summan och sedan visar varningen dem på skärmen.
I detta primitiva exempel är användningen av en återuppringning i första hand en principdemonstration. Man kan helt enkelt kalla återuppringningarna som vanliga funktioner, calcProduct(num1, num2) . Återuppringningar används vanligtvis när funktionen behöver utföra händelser innan återuppringningen exekveras, eller när funktionen inte (eller inte kan) ha meningsfulla returvärden att agera på, vilket är fallet för asynkron JavaScript (baserat på timers) eller XMLHttpRequest - förfrågningar . Användbara exempel kan hittas i JavaScript-bibliotek som jQuery där .each()-metoden itererar över ett arrayliknande objekt, det första argumentet är en callback som utförs vid varje iteration.
Röd och REBOL
Från JavaScript ovan, här är hur man skulle implementera samma i antingen REBOL eller Red (programmeringsspråk) . Lägg märke till den renare presentationen av data som kod.
- retur är underförstått eftersom koden i varje funktion är den sista raden i blocket
- Eftersom varningen kräver en sträng, producerar form en sträng från resultatet av calculate
- Få-ordet! värden (dvs :calc-product och :calc-sum) triggar tolken att returnera koden för funktionen i stället för att utvärdera med funktionen.
- Datatypen! referenser i ett block! [flyta! heltal!] begränsa typen av värden som skickas som argument.
Röd [ Titel: "Callback exempel" ] beräkna: func [ num1 [ nummer! ] nummer2 [ nummer! ] återuppringningsfunktion [ funktion! ] ][ callback-funktion num1 num2 ] calc-product: func [ num1 [ nummer! ] nummer2 [ nummer! ] ][ num1 * num2 ] calc-summa: func [ num1 [ nummer! ] nummer2 [ nummer! ] ][ num1 + num2 ] ; varningar 75 , produkten av 5 och 15 varningsformulär beräkna 5 15 :calc-product ; varningar 20, summan av 5 och 15 varningsformulär beräkna 5 15 : calc -summa
Lua
Ett exempel på färginterpolering som använder Roblox- motorn som tar en valfri .done-återuppringning:
0
0 0
vänta ( 1 ) lokal DT = vänta ( ) funktion tween_color ( objekt , finish_color , fade_time ) local step_r = finish_color . r - objekt . Bakgrundsfärg3 . r local step_g = finish_color . g - objekt . Bakgrundsfärg3 . g lokal steg_b = finish_color . b - objekt . Bakgrundsfärg3 . b local total_steps = 1 / ( DT * ( 1 / fade_time )) local completed ; coroutine.wrap ( funktion () för i = , 1 , DT * ( 1 / fade_time ) do object . BackgroundColor3 = Color3 . new ( object . BackgroundColor3 . r + ( step_r / total_steps ), object . BackgroundColor3 . g + ( step_g / total_steps ), objekt Bakgrundsfärg3 . b + ( step_b / total_steps ) ) vänta ( ) slut om det är klart sedan slutfört ( ) slut slut ) () return { done = function ( callback ) completed = callback end } end tween_color ( some_object , Color3 . ny ( 1 , , ), 1 ). klar ( funktion () skriv ut "Färginterpolering klar!" slut )
Pytonorm
En typisk användning av callbacks i Python (och andra språk) är att tilldela händelser till UI-element.
Här är ett mycket trivialt exempel på användningen av en callback i Python. Definiera först två funktioner, återuppringning och anropskoden, skicka sedan återuppringningsfunktionen till anropskoden.
>>> def get_square ( val ): ... """Återuppringningen.""" ... return val ** 2 ... >>> def caller ( func , val ): ... return func ( val ) ) ... >>> uppringare ( get_square , 5 ) 25
Julia
Funktioner i Julia är förstklassiga medborgare , så de kan helt enkelt överföras till funktioner på högre nivå för att användas (kallas) i kroppen av dessa funktioner.
Här är samma exempel ovan i Julia:
julia> get_square ( val ) = val ^ 2 # Återuppringningen get_square (generisk funktion med 1 metod) julia> caller ( func , val ) = func ( val ) caller (generisk funktion med 1 metod) julia> caller ( get_square , 5 ) 25
Se även
- Kommandomönster
- Fortsättningspasserande stil
- Händelseloop
- Händelsestyrd programmering
- Implicit anrop
- Inversion av kontroll
- libsigc++ , ett callback-bibliotek för C++
- Signaler och slots
- Användaravsluta
externa länkar
- Grundläggande instinkter: Implementera återuppringningsmeddelanden med hjälp av delegater
- Implementera callback-rutiner i Java
- Implementera Script Callback Framework i ASP.NET
- Använda C++-medlemsfunktioner med C-bibliotek (arkiverat från originalet den 6 juli 2011)
- Stilfallsstudie #2: Generiska återuppringningar