Exceptions i Delphi Exceptions er en teknik til at fange fejl under programafviklingen. Ikke programmeringsfejl, men fejl der opstår i forskellige situationer, f.eks. en fil der mangler en fil der er skrivebeskyttet, så skrivning er umulig forsøg på i en database at tilføje en post, som findes i forvejen brugeren har indtastning ulovligt input Exceptions er en forholdsvis ny ting inden for programmering. I C++ blev det først implementeret i 1992. I Delphi kom det allerede i den første version Delphi 1. Det visuelle component bibliotek (VCL) og andre units i Delphi er programmeret under anvelse heraf. Try Ved brug af edit-komponenter skal man ofte konvertere mellem typerne string og integer. Til en sådan konvertering er det utrolig bekvemt at benytte IntToStr og StrToInt i stedet for de gammelkte Str- og Val-funktioner fra TurboPascal. IntToStr volder naturligvis ingen problemer; medens StrToInt går galt såfremt strengen, der konverteres, ikke indeholder et lovligt helttal. Hvor Str-funktionen havde en returparameter, hvis værdi angav om konverteringen gik godt, genererer StrToInt en ion, såfremt konverteringen går galt. (Der findes ingen god, dansk oversættelse for ion). Nedenfor vises forskellen mellem de to måder at behandle fejlen på. Med Str: Str(Edit1.Text,t1,code); if code<>0 then showmessage('du har ikke angivet en lovlig talværdi'); og med StrToInt: t1:=strtoint(edit1.text) showmessage('du har ikke angivet en lovlig talværdi'); Er der tre strenge, der skal konverteres, og man ikke bekymrer sig om hvilken, der evt. ikke kan konverteres, bliver koden klart simplere med ion handling: 1
t1:=strtoint(edit1.text); t2:=strtoint(edit2.text); t3:=strtoint(edit3.text); sum:=t1+t2+t3 showmessage('et af felterne indeholder ikke lovlig talværdi'); Med brug at Str-funktioner bliver koden temmelig kluntet, f.eks.: Str(Edit1.Text,t1,code1); Str(Edit2.Text,t2,code2); Str(Edit3.Text,t3,code3); if (code1<>0) or (code2<>0) or (code3<>0) then showmessage('du har ikke angivet lovlig talværdi') else sum:=t1+t2+t3; Programmering med ions ser nok anderledes ud; men er faktisk tæt ved en naturlig tankegang for situationen: Skriv kode uden at tænke på evt. fejl, hvis det går galt, så gør et eller andet. og man slipper for if-then-else konstruktioner og testvariable eller booleans. Dette fremgår tydeligt af det følge eksempel. Det er et realistisk og eksistere eksempel til beregning af antal hele planker, der skal anves til en limtræsproduktion på en møbelfabrik. Funktionen DivWithRoundUp foretager div-operationen, men runder op til nærmeste hele tal, når divisionen ikke går op. Efter beregningen skal TempStr indeholde resultatet som en tekststreng eller.., hvis beregningen ikke kunne foretages p.g.a. mangle data. I gammeldags programmering ser det således ud. SpildPct:=SKData.LookUpInt(12); LamelPrRB:=trunc(RealValue[10]/(RealValue[7]+CutWidth)); ion:=(integervalue[13]=0) or (LamelPrRB=0); if not ion then begin HelePl:=DivWithRoundUp(FremStilAntal,IntegerValue[13]); AntalKapLgd:=DivWithRoundUp(HelePl*IntegerValue[5],LamelPrRB); SpildAntal:=round(AntalKapLgd*SpildPct/100+0.5); inc(antalkaplgd,spildantal) else AntalKapLgd:=0; if ion then TempStr:='..' else Str(HelePl,TempStr); Med ion handling ville det se således ud: 2
SpildPct:=SKData.LookUpInt(12); LamelPrRB:=trunc(RealValue[10]/(RealValue[7]+CutWidth)); HelePl:=DivWithRoundUp(FremStilAntal,IntegerValue[13]); AntalKapLgd:=DivWithRoundUp(HelePl*IntegerValue[5],LamelPrRB); SpildAntal:=round(AntalKapLgd*SpildPct/100+0.5); inc(antalkaplgd,spildantal) TempStr:=IntToStr(HelePl); AntalKapLgd:=0; TempStr:='..' ; Som det ses er brug af en boolean (ion) og alle if-then-else forsvundet og blot erstattet af et enkelt -. Strukturen er klart forbedret. Ja faktisk fanger det sidste en fejl som den første udgave ikke gør. Hvis RealValue[7]+CutWidth er 0, opstår en division med 0, som det første eksempel ikke tager højde for. Så den sidste version med ion er også på dette punkt bedre. Når der i en -- struktur opstår en ion mellem og, sker der straks et hop til sætningerne efter og disse udføres. Try finally Try-- giver ikke mulighed for evt. oprydning både efter fejl og uden fejl. Derfor har Delphi en anden type ion handling, -finally-. Den bruges f.eks. i forbindelse med filer og lagerallokering. Følge eksempel viser ion handling i forbindelse med skrivning til fil. AssignFile(f,TheFileName); App(f); writeln(f,'datetimetostr(now)); writeln(f,fornavn); writeln(f,efternavn); writeln(f,antaltimer); finally CloseFile(f) ; Hvis en af writeln-sætninger giver anledning til fejl, sker der straks et hop til koden efter finally, dvs. filen lukkes. Hvis skrivningen går godt udføres koden efter finally også. Filen lukkes altså korrekt i alle tilfælde. Forskellen på - og -finally er altså, at koden efter kun udføres, når der er opstår en ion mellem og, medens koden efter finally udføres i alle tilfælde. En -finally blok kaldes på engelsk ressource protection block i modsætning til en - blok, der kaldes ion block. 3
Alle fejl behandles med ion I Delphi er ion handling som nævnt gennemført totalt. Enhver fejl på run-time håndteres v.h.a. ions, og alle ions fanges. Hvis programmøren ikke selv har skrevet kode til behandling af en ion, fanger Delphi den og viser fejlen i en boks. Enhver blok i programmet er omfattet af ion handling. Hvis der opstår en fejl i en blok kode, afbrydes udførelsen af blokken. Det kan man f.eks. overbevise sig om, ved at skrive en Clik-procedure som følge. procedure TForm1.Button1Click(Ser: TObject); var x,y: real; begin x:=7.0; showmessage('før'); y:=x/0.0; showmessage('efter'); ; Når Click-proceduren udføres bliver den sidste ShowMessage aldrig udført, fordi der opstår en division med 0 inden. På den anden side stoppes programmet heller ikke helt; men der kommer en meddelelse fra Delphi om fejlen. Hvorefter man med Run kan fortsætte programafviklingen. Når programmet afvikles fra udviklingssystemet, kan man da bestemme, om Delphis udviklingssystem straks skal vise fejlen eller vente på programmet evt. selv fanger fejlen og viser den. Indstillingen af hvordan fejl fanges varierer lidt med de forskellige versioner af Delphi. I Delphi3: Det indstilles i Tools Environment Options: Figur 1 4
I Delphi 4 og 5 sker det i Tools Debugger Options: Figur 2 Hvis den viste CheckBox er afkrydset, vil udviklingssystemet straks fange fejlen: Figur 3 Når du vælger Run for at fortsætte programafviklingen, vil programmet selv fange fejlen: Figur 4 Hvis programmøren selv har skrevet en - til håndtering af fejlen, vil boksen figur 3 blive erstattet af programmørens egen håndtering af fejlen. 5
Hvis man undlader at afkrydse Break on ion, er man fri for at skulle kke på Run efter fejl, for at lade programmet fortsætte. Til gengæld kan man risikere ikke at blive informeret om fejl. Exceptionklassen En ion i Delphi er et objekt af typen Exception. Med Delphis browser kan man studere denne klasse: Figur 5 Heraf fremgår at Exception er en nedarvning fra TObject, samt at der findes mange klasser nedarvet fra Exception, f.eks. EIntErrror, der har EDivByZero, EIntOverFlow og ERangeError som yderligere nedarvede klasser. Man kan skrive ion blokke og resource protection blokke inde i hinanden, og man kan angive, at en ionblok kun skal reagere på en speciel fejl: q:=(x div y)*z on EDivByZero do showmessage('division med nul') Hvis den genererede ion ikke er af typen EDivByZero, vil den går videre til den omligge blok eller procedure, der kaldte denne kode. Findes der ingen blok, der håndterer den pågælde ion, tager Delphi sig af den. Selvom et sted i programmet håndterer en ion, kan det ske at en omligge blok i programmet, skal tage sig af samme ion. Ved brug af Raise kan den ses videre: q:=(x div y)*z on EDivByZero do begin showmessage('division med nul'); 6
raise ; Egne ion Da ion er et object fra ionklassen, kan programmøren selv definere nye ions og generere en sådan. Det er faktisk ret simpelt. Hvis man har en meget kompliceret konvertering af en tekst fra en edit-komponent, kan man f.eks. definere en ion med navn EMinKonvFejl: type EMinKonvFejl=Class(Exception) I konverteringsproceduren kan man så i tilfælde af fejl skabe en ion if not Ok then raise EMinKonvFejl.Create('Den fejl du ved nok') Delphi sørger selv for nedlæggelse af objektet, når den pågælde ion er håndteret. Teksten Den fejl du ved nok skrives i messageboksen i figur 3, såfremt Stop/Break on ion er slået til. Hvis Stop/Break on ion er slået fra, og programmet ikke selv håndterer den selvdefinerede ion, kan man slet ikke se, at der var en ion. Derfor er det en fordel, at Break on ion er slået til under debugging af programmer med selvdefinerede ion. Resume For professionelle udviklere giver brug af ion en række fordele: C mulighed for skrive robuste programmer, som reagerer fornuftigt i fejlsituationer bedre sammenhæng mellem selve koden og fejlhåndteringen C en ensartet metode til fejlhåndtering i stedet for forskellige ad hoc metoder øget læselighed C lettere vedligeholdelse [Teix] i litteraturlisten i bogen indeholder et afsnit om ion. 7