Callback procedurer

Tags:    delphi
Skrevet af Bruger #270 @ 20.08.2001
Callback procedurer

Introduktion

Værdien af Callback procedurer og funktioner, i det følgende blot refereret til som Callback procedurer, er ofte stærkt undervurderet - selv om fordelene ved brugen af dem er åbenlyse. For eksempel kan de være med til at undgå redundant kode, samt gøre din kode nemmere at læse, eller måske vigtigere: nemmere at håndtere.

Hvad er Callback?

Callback betyder at medgive en procedure (eller funktion) som parameter til en anden procedure. Når så en bestemt hændelse sker i den procedure du kaldte, så vil den automatisk kalde callback proceduren, som du medgav som parameter. Når så callback proceduren er færdig med sit arbejde leverer den kontrollen tilbage til den oprindelige procedure som du kaldte.

Et eksempel

Lad os sige du har et array med nogle objekter i, og du tit ønsker at kalde en bestemt metode på alle objekterne i arrayet. Dette er nemt gjort, ved blot at køre hele arrayet igennem og kalde den ønskede metode på alle objekterne. Hvis vi så nu forestiller os, at du har fem forskellige metoder som du ønsker at kalde på alle objekterne i arrayet på forskellige tidspunkter. Igen nemt gjort, med en omgang cut and paste, men her ville du opleve en stor fordel ved at bruge en callback procedure. Alt hvad det er nødvendigt at skrive for at få det her til at lykkes er en masterprocedure, der looper igennem hele objektarrayet, og for hver enkelt af dem kalde den callback procedure du medgav masterproceduren som parameter.

Olie på fingerne

Det er nu tid til at blive lidt mere konkret, smøge ærmerne op og få olie på fingerne. Dette eksempel vil vise hvordan man erklærer en callback procedure, og hvordan man bruger den. Den konkrete rutine bruges til at gennemsøge biblioteker og kalde callback proceduren hver gang en fil bliver fundet.
Første skridt er at erklære en datatype i interface sektionen af vores unit for at fortælle, hvad callback proceduren er:
TFileCallbackProcedure = procedure (FileName: String) of object;
Hvis vi skal oversætte det til lidt mere dansk så siger vi her at TFileCallbackProcedure er en procedure der tager en string som argument og er en metode på et objekt. Det er den sidste del of object, der siger at proceduren ligger som metode på et objekt og ikke bare løst.
Næste skridt er at erklære en procedure der automatisk kalder vores callback procedure:
procedure RecurseDirectory(Dir: String;
                           IncludeSubs: boolean;
                           Callback: TFileCallbackProcedure
                          );
Ikke mere løs snak, lad os nu se noget rigtig kode:
procedure TForm1.RecurseDirectory(Dir: String;
                                  IncludeSubs: boolean;
                                  Callback: TFileCallbackProcedure);
var
  SearchRec : TSearchRec;
  FindResult: LongInt;
begin
  FindResult := FindFirst(Dir + '\\*.*', faAnyFile , SearchRec);
  while FindResult = 0 do
  begin
    { Sikrer at det ikke er . eller .. bibloteket vi har fundet }
    if not(SearchRec.name[1]='.') then
    begin
      if (SearchRec.attr and faDirectory) <> 0 then
      begin
        { Det er en folder så kald rekursivt, hvis IncludeSubs er sand }
        if IncludeSubs then
          RecurseDirectory(Dir +'' + SearchRec.name, IncludeSubs, Callback);
      end
      else
        { Kald vores callback procedure }
        callback(dir + '' + SearchRec.name);
    end; // if not...

    FindResult := FindNext(SearchRec);
  end;
end;
Jeg vil ikke forklare hele funktionen i detaljer her, da denne artikel ikke har til opgave at forklare rekursion, men derimod callback procedurer. Hovedideen i metoden ovenfor er, at den scanner igennem det bibliotek den fik ind som argument, og hvis den finder en fil så kalder den callback proceduren (callback argumentet), med filnavn og den fulde sti som parameter. Den rekursive del bliver kun kaldt, hvis IncludeSubs er sandt og der er tale om et bibliotek - i såfald virker det ved at metoden kalder sig selv med det nye bibliotek. For en nærmere forklaring af fordele og ulemper ved rekursion, samt hvordan det virker, se artiklen "En begynders guide til rekursion".
Alt der nu er tilbage er, at skrive callback proceduren og kalde metoden RecurseDirectory.
Start en ny Applikation, placer en knap (TButton) og en ListBox (TListBox) på din form, for nemheds skyld bruger vi bare de navne til komponenterne som Delphi udstyrer os med. Opret og implementer her efter RecurseDirectory, som vist tidligere.
Nu mangler vi bare at definere og skrive en simpel callback procedure. Skriv definitionen i private delen af din form (kan ændres til Public delen om nødvendigt).
type
  TForm1 = class(TForm)
    .....
  private
    { Private declarations }
    procedure MyCallback(FileName: String);
  public
    { Public declarations }
    .....
  end;
Det eneste vi vil gøre med de fundne filer i dette simple eksempel, er at stoppe dem alle i en ListBox:
procedure TForm1.MyCallback(filename: String);
begin
  ListBox1.Items.Add(FileName);
end;
For at få det hele til at virke skal vi have kaldt RecurseDirectory proceduren. Det gøres helt som vanligt i Button1s OnClick event:
procedure TForm1.Button1Click(Sender: TObject);
begin
  RecurseDirectory('c:', true, MyCallback);
end;
Alt der sker her er at vi kalder RecurseDirectory proceduren, som for hver fil den finder kalder vores Callback procedure (MyCallback), som så lister dem op i vores ListBox. Vores lille applikation skulle nu være parat til at køre. Når du kører den skulle den gerne begynde at liste filnavne op fra dit c drev, når du trykker på knappen (Button1). Hvis du som mig har et C drev med flere tusinde filer, kan det godt tage et stykke tid. :-(

Konklusion

Jeg håber meget at du har kunne bruge denne artikel til noget fornuftigt, samt at du kan se fordelen ved at bruge Callback procedurer i dine applikationer. Eksemplet, der blev brugt i denne artikel ar meget simpelt, men det kan nemt udvides, og det er kun din fantasi, der sætter grænsen. Du kunne feks.

Tælle antallet af pas filer i en folder.
Slette backup filer.
osv.

Eller hvis du rigtig har fået blod på tanden, og vil lave noget mere ambitiøst, kunne du loope gennem din Applikation, og hente/gemme deres placering i registreringsdatabasen. Mulighederne er uendelige, men Callback procedurer er ikke altid den bedste tilgang til et problem (f.eks. Hvis du kun har én Callback procedure), men det er rart at vide hvordan de fungerer og vide at du kan lave dem.


Hvad synes du om denne artikel? Giv din mening til kende ved at stemme via pilene til venstre og/eller lægge en kommentar herunder.

Del også gerne artiklen med dine Facebook venner:  

Kommentarer (0)

Du skal være logget ind for at skrive en kommentar.
t