Concepte avansate în programarea Object Pascal
În aceasta sectiune ne propunem sa luam în discutie o serie de aspecte care subliniaza pregnant noutatea ofertei Delphi, în principal, în materie de programare obiect orientata. Se va putea observa la finalul sectiunii, efortul facut de cei care au specificat Object Pascal, pentru a apropia acest limbaj de standardele de facto în programarea obiect orientata si, totodata, de a pastra proprietatile limbajului Pascal specificate de primul lui parinte Niklaus Wirth.
3.1 Clase în Object Pascal
Programatorii Pascal îsi amintesc de faptul ca, ceea ce în modelarea obiect orientata se numeste clasa, în Pascal are ca echivalent tipul obiect. Ca forma de comunicare a ideilor referitoare la solutia obiect orientata a unei probleme, tipul obiect poate fi utilizat ca substitut pentru clasa. Din punct de vedere al sintaxei de definire a unei clase, vom vedea ca, în Object Pascal s-a adoptat cuvântul cheie class în locul cuvântului cheie object, pentru a defini o clasa. Coceptul de clasa în programarea Object Pascal introduce si o serie de alte elemente care fac din programarea pe obiecte un instrument deosebit de puternic, în realizarea de aplicatii fiabile, extensibile, puternic interactive, inclusiv în faza de proiectare asistata de mediul Delphi. Extensia cheie a limbajului Object Pascal pentru marirea interactivitatii aplicatiilor Delphi se refera la introducerea conceptului de proprietate.
O data de tip class este o structura care se compune dintr-un numar fix de componente(membri). Componentele posibile ale unei clase sunt: câmpurile, metodele si proprietatile. Spre deosebire de alte tipuri, un tip class poate fi declarat doar în sectiunea de declarare a tipurilor cea mai exterioara a programului sau unit-ului. Astfel ca, un tip class nu poate fi declarat în sectiunea de declarare a variabilelor sau în interiorul procedurilor, functiilor sau în blocul de definitie al metodelor.
Sintaxa de declarare a tipului class este:
<TClasa_Utilizator>=class [(<TClasa_Definitoare>)]
public
<Nume_Camp>:<Tip_Camp>;
.
<Antet_Metoda>
.
property
<Nume_Proprietate>:<Tip_Proprietate>
read <Nume_Camp/Nume metoda>
write < Nume_Camp/Nume metoda>
private
:
protected
published
end;
Domenii de vizibilitate
Componentele unei clase pot avea domenii de vizibilitate diferite, în functie de directiva de specificare a vizibilitatii sub a carei incidenta se afla. Dupa cum se vede si mai sus, aceste directive de specificare a vizibilitatii sunt: private, protected, public si published. Aceste directive restrictioneaza vizibilitatea componentelor în situatia în care alte unit-uri forteaza accesul la ele. În interiorul unit-ului în care este definita clasa, toate componentele sunt vizibile.
Observatie
Spre deosebire de multi alti identificatori utilizati în declararea tipurilor, directivele de specificare a vizibilitatii nu sunt cuvinte rezervate în afara contextului care ocazioneaza declararea clasei. În practica se recomanda, totusi, sa le tratam ca si când ar fi cuvinte rezervate, în adevaratul sens al cuvântului.
Directivele de specificare a vizibilitatii nu trebuie mentionate explicit pentru fiecare membru al clasei. Regula este ca dupa o directiva de specificare a vizibilitatii sa urmeze toti membrii care au acelasi tip de vizibilitate. Un domeniu de vizibilitate se termina în momentul în care este întâlnita alta directiva de specificare a vizibilitatii.
Specificatorul de vizibilitate private
Acest specificator este cel mai restrictiv. Membrii privati ai unei clase nu se vad în afara unit-ului în care este definita clasa. De obicei se declara ca private componente ale caror valori sunt critice pentru comportamentul instantelor clasei respective. Daca avem nevoie de doua clase care trebuie sa aiba acces reciproc la componentele lor, atunci aceste clase se vor defini în acelasi unit. Detaliile de implementare sunt complet ascunse fata de end-user.
Specificatorul de vizibilitate protected
Specificatorul de vizibilitate protected relaxeaza restrictiile de vizibilitate, în sensul ca, componente declarate ca protected într-o clasa pot fi accesate de obiecte care fac parte din domeniul clasei; altfel spus, descendentii unei clasei au acces la componentele din sectiunea protected a acesteia. Detaliile de implementare ramân în continuare ascunse fata de end-user.
Specificatorul de vizibilitate public
Componentele publice sunt vizibile oriunde este vizibila si clasa. Nu se aplica alte restrictii asupra componentelor publice ale unei clase.
Specificatorul de vizibilitate published
Declararea unei componente ca fiind de tip published, informeaza compilatorul ca trebuie sa genereze informatii de tip run-time (RunTime Type Information-RTTI) pentru componenta respectiva.
Informatiile de tip run-time permit unei aplicatii externe sa afle date despre câmpurile, metodele sau proprietatile unui obiect, despre clasa definitoare a obiectului, etc.
Pe timpul executiei unui program, din punct de vedere al vizibilitatii, o componenta published se comporta ca si când ar fi o componenta de tip public. Toate componentele published sunt vizibile în orice unit unde clasa care le încapsuleaza este vizibila. Diferenta consta în faptul ca, o aplicatie externa poate obtine informatii de tip run-time despre componentele published.
Ca un exemplu, inspectorul de obiecte al sistemului Delphi foloseste interfetele published ale obiectelor din Paleta de Componente a sistemului Delphi pentru a determina ce proprietati si evenimente trebuie vizualizate în timpul proiectarii unei aplicatii.
Exista anumite restrictii în ceea ce priveste modul de utilizarea al directivei published.
O clasa poate avea componente published daca este compilata sub incidenta directivei având starea sau daca este derivata dintr-o clasa care deja a fost compilata astfel încât sa aiba componente published. Directiva controleaza generarea RTTI.
Un câmp definit într-o sectiune published trebuie sa fie de tip clasa. Câmpurile de orice alt tip trebuie plasate în sectiunile public, protected sau private.
Proprietatile published sunt restrictionate din punct de vedere al tipului. Sunt acceptate proprietati de tip:
-ordinal;
-real(Single, Double, Extended, Comp, nu si real);
-sir de caractere;
-multime (small set);
-clasa;
-identificator de metoda (dar nu tip procedural global)
Small set desemneaza un tip multime al carui tip de baza este ordinal si ale carui valori ordinale sunt cuprinse între 0 si 50.
Metode
În procesul de definire a unei clase, metodele leaga codul cu tipurile de date pe care codul este abilitat sa le manipuleze. În acest scop, metodele au dreptul sa acceseze câmpurile unui obiect fara ca acestea sa-i fie trimise explicit ca parametri.
Declararea unei metode are doua parti: declararea signaturii metodei în procesul de definire a clasei si implementarea corpului metodei în afara definitiei clasei, dar în acelasi unit în care s-a definit si clasa. De la programarea Pascal OO se stie ca declararea signaturii este asemanatoare unei declaratii forward de procedura sau functie. De subliniat ceea ce, de asemenea, se stie de la Pascal OO si anume faptul ca implementarea corpului metodei presupune ca antetul ei sa specifice numele clasei din a carei definitie face parte metoda. Referitor la implementarea unei metode mai facem urmatoarele precizari:
În interiorul corpului unei metode, o instructiune care apeleaza o functie sau o procedura permite utilizarea designatorilor de metoda calificata pentru a activa o anumita metoda a unei clase. Un designator de metoda poate fi o variabila referinta la un obiect sau o referinta la o clasa. Cuvântul rezervat inherited desemneaza stramosul tipului obiect care include metoda. Prezentam în continuare un exemplu de definire a unei clase împreuna cu implementarea metodei aferente în care se utilizeaza si cuvântul rezervat inherited.
type
TFramedLabel = class(TLabel)
protected
procedure Paint; override;
end;
procedure TFramedLabel.Paint;
begin
inherited Paint;
with Canvas do
begin
Brush.Color := clWindowText;
Brush.Style := bsSolid;
FrameRect(ClientRect);
end;
end
În interiorul blocului care corespunde implementarii unei metode, regulile de vizibilitate sunt, dupa cum poate ca s-a înteles, diferite de regulile de vizibilitate pentru proceduri si functii oarecare. Accesul la componentele unui obiect din interiorul unei metode este simplificat datorita inserarii automate în codul unei metode a unei calificari de tipul:
with Self do
begin
...
end
Variabila Self este o variabila de acelasi tip cu clasa în care apare metoda.
Implicit metodele sunt statice.
Object Pascal propune însa si alte tipuri de metode, din perspectiva modului de rezolvare a apelului acestora. Le prezentam în Tabelul 3.1.
Directiva |
Efect |
Virtual |
Metoda apelata este determinata cu ajutorul tabelei VMT |
Dynamic |
Metoda apelata este determinata cu ajutorul tabelei de metode dinamice |
Message |
Metoda apelata este determinata cu ajutorul mecanismului de transmitere a mesajelor |
Abstract |
Metoda nu are implementare dar trebuie redefinita în aval |
Override |
Metoda redefineste o metoda virtuala sai dinamica |
Class |
Apel de metoda fara a utiliza variabila implicita Self |
Tabelul 3.1 Tipuri de metode în Object Pascal
Evident, în Object Pascal pot fi metode :procedurile, functiile, constructorii si destructorii. În linii mari, constructorii si destructorii se utilizeaza cu semnificatia care se cunoaste de la limbajul Pascal.
Metode statice
Metodele declarate într-o clasa sunt implicit statice. Nu este necesar un identificator special pentru a desemna o metoda de tip static. Când este apelata o metoda statica, tipul variabile este cel care determina ce metoda urmeaza sa fie executata. Compilatorul determina exact adresa metodei în timpul compilarii.
Avantajul fundamental al metodelor statice consta în faptul ca executarea lor este foarte rapida. Prin contrast, metodele virtuale si dinamice rezolva în timpul executiei problema legarii codului care trebuie executat, ceea ce lungeste timpul de executie al programelor daca se abuzeaza de apelul la metode virtuale sau dinamice.
O metoda statica nu poate fi schimbata când este mostenita de un descendent al clasei în care aceasta este definita. Altfel spus, daca declarati o clasa care include în definitie o metoda statica, atunci derivând o noua clasa din aceasta, clasa derivata partajeaza exact aceeasi metoda, situata la aceeasi adresa. Aceasta înseamna ca nu putem redefini static metodele.
În exemplul de mai jos, prima clasa declara doua metode statice. Cea de-a doua clasa declara doua metode statice cu acelasi nume, care înlocuiesc metodele mostenite de la prima clasa.
type
TFirstComponent = class(TComponent)
procedure Move;
procedure Flash;
end;
TSecondComponent = class(TFirstComponent)
procedure Move;
function Flash(HowOften: Integer): Integer;
end;
Metode virtuale
Spre deosebire de apelul metodele statice, apelurile metodelor virtuale nu sunt rezolvate în faza de compilare. Metoda care urmeaza sa fie executata, în cazul virtual este determinata în timpul executiei printr-un procedeu numit legare întârziata. Orice metoda poate fi facuta virtuala prin mentionarea unei directive virtual la sfârsitul definitiei metodei în lista de componente a clasei. Noutatea metodelor virtuale consta în urmatorul mecanism: daca într-un lant de derivare, o anumita metoda, declarata virtuala în clasa radacina, apare în descendenti redefinita (marcata de directiva override), atunci un apel la metoda respectiva, facut din contextul unei variabile compatibila cu lantul de derivare, va fi rezolvat în functie de tipul curent al variabilei. Tipul variabilei este stabilit în momentul crearii instantei, de catre constructorul corespunzator clasei context.
Alegerea metodei corespunzatoare contextului se face cu ajutorul tabelelor VMT, unice, asociate claselor care au metode virtuale si create la apelarea constructorilor.
Metode dinamice
Un mecanism oarecum similar metodelor virtuale este utilizat si în cazul metodelor dinamice. Din punctul de vedere al utilizatorului nu se poate face diferenta între cele doua tipuri de metode. Codul generat, însa, difera semnificativ, astfel: apelurile metodelor virtuale sunt mai rapide dar genereaza cresterea codului; apelurile metodelor dinamice sunt mai lente, dar genereaza cod mai mare.
În mod uzual, metodele virtuale sunt preferate daca se doreste exploatarea capabilitatilor polimorfice ale unui lant de derivare. Daca, însa, se doreste crearea unei clase de baza din care se deriveaza un numar mare de descendenti, si în fiecare descendent se redefineste un numar mic de metode virtuale mostenite.
Metode de tip mesaj
Aceste metode sunt o forma specializata a metodelor dinamice, fiind indispensabile în manipularea eficienta a mesajelor unei aplicatii Delphi. Compilatorul Object Pascal genereaza cod de apel specific acestor metode daca sunt declarate cu directiva message. Mecaismul de apel utilizat este aproape identic cu cel utilizat în cazul metodelo dinamice. Exista, însa, si elemente specifice de care programatorul trebuie sa tina cont când utilizeaza aceste tipuri de metode. Aceste elemente specifice sunt legate de protocolul de gestiune a mesajelor unei aplicatii într-o aplicatie Windows, în genere si într-o aplicatie Delphi, în particular.
Prezentam, în continuare, un exemplu de specificare a unui handler utilizator de mesaje.
const
CM_CHANGECOLOR = WM_USER + 400;
type
TMyComponent = class(TControl)
...
protected
procedure CMChangeColor(var Message: TMessage): message
CM_CHANGECOLOR;
.
end;
procedure TMyComponent.CMChangeColor(var Message: TMessage);
begin
Color := Message.lParam;
inherited;
end
Recomand studierea atenta a help-ului on-line Delphi si a exemplelor demonstrative, pentru a aprofunda problematica lucrului cu mesajele în aplicatiile Delphi.
Metode abstracte
De regula când se specifica o metoda în lista de componente asociata definitiei unei clase, compilatorul se asteapta sa gaseasca implementarea metodei în sectiunea corespunzatoare a unit-ului gazda. Pe de alta parte, atunci când se specifica o metoda în clasa de baza, se stabileste un comportament implicit al tuturor descendentilor, relativ la metoda respectiva. Pentru a schimba comportamentul în descendenti, trebuie redefinita metoda în descendenti. Daca nu are sens asocierea în clasa de baza a unui comportament implicit, se poate utiliza directiva abstract, pentru a semnala compilatorului ca nu va fi implementata o metoda implicita. De precizat faptul ca orice metoda declarata abstracta trebuie sa fi, totodata virtuala sau dinamica, ca în exemplul de mai jos.
type
Base = class
procedure DaliVision; virtual; abstract;
procedure TellyVision; dynamic; abstract;
end;
Metode redefinite (override)
Atunci când logica unui lant de derivare impune redefinirea unor metode ( virtuale sau dinamice), în descendenti se anunta redefinirea cu ajutorul directivei override. Pentru a ilustra ideea, prezentam si exemplul de mai jos.
type
TFirstComponent = class(TCustomControl)
procedure Move;
procedure Flash; virtual;
procedure Beep; dynamic;
end;
TSecondComponent = class(TFirstComponent)
procedure Move;
procedure Flash; override;
procedure Beep; override;
end;
Metode de tip clasa
Metodele de tip clasa nu sunt altceva decât metode ce pot fi apelate ca orice alte proceduri sau functii obisnuite, fara a fi nevoie de vreo instantiere a clasei care le-a definit. Acest tip de metode sunt usor de recunoscut prin prefixarea lor cu directiva class ca în exemplul de mai jos.
Trebuie retinut însa ca metodele de tip clasa nu trebuie sa modifice informatii relative la instante. Astfel ca, în exemplul:
type
Tclass1 = class
Nume:string;
class procedure ModificNume (NumeNou:string);
end;
class procedure ModificNume (NumeNou:string);
begin
Nume:=NumeNou;
end
compilatorul va semnala eroare deoarece metoda de tip clasa ModificNume încearca sa modifice valoarea câmpului Nume, care este o variabila de instanta a clasei Tclass1. Unit-ul System defineste doua tipuri, TObject si TClass, care sunt tipuri radacina pentru toate tipurile clasa si referinta la clasa,despre care vom discuta în continuare. Radacina TObject contine o bogata colectie de metode de tip clasa pe care le putem apela fara a fi nevoie sa instantiem clasa.
type
TObject = class;
TClass = class of TObject;
TObject = class
constructor Create;
destructor Destroy; virtual;
class function ClassInfo: Pointer;
class function ClassName: ShortString;
class function ClassNameIs(const Name: string): Boolean;
class function ClassParent: TClass;
function ClassType: TClass;
procedure CleanupInstance;
procedure DefaultHandler(var Message); virtual;
procedure Dispatch(var Message);
function FieldAddress(const Name: ShortString): Pointer;
procedure Free;
procedure FreeInstance; virtual;
class function InheritsFrom(AClass: TClass): Boolean;
class function InitInstance(Instance: Pointer): TObject;
class function InstanceSize: Longint;
class function NewInstance: TObject; virtual;
class function MethodAddress(const Name: ShortString): Pointer;
class function MethodName(Address: Pointer): ShortString;
end;
Conceptul de proprietate
Conceptul de proprietate nu este absolut nou. În Turbo Pascal, proprietatile se confundau cu înregistrarile sau câmpurile unui obiect. În Delphi, notiunea de proprietate, ca si concept nou, pare sa fie strâns legata de necesitatile programarii vizuale. Este adevarat doar în parte deoarece notiunea de proprietate are o valoare de întrebuintare care depaseste cerintele programarii vizuale. O definitie de proprietate într-o clasa permite declararea unui anumit atribut al obiectelor unei clase si a actiunilor asociate cu citirea si scrierea atributelor. Exemple de astfel de proprietati sunt : proprietatea "Caption" a unei forme, marimea fontului intr-un derivat TMemo, etc.
Putem spune ca proprietatile sunt o extensie naturala a câmpurilor unui obiect. Atât câmpurile cât si proprietatile pot fi utilizate pentru a exprima atributele unui obiect, dar, în timp ce câmpurile sunt doar locatii de memorie care pot fi examinate si modificate dupa dorinta, proprietatile asigura un control mai mare asupra valorilor atributelor, datorita mecanismelor de citire /scriere cu care se asociaza o anumita proprietate.
Prezentam mai jos, cu ajutorul limbajului diagramelor de sintaxa, regulile sintactice care stau la baza specificarii proprietatilor unei clase.
Definitie proprietate
![]() |
Definitie interfata proprietate
![]() |
Definitie lista parametri proprietate
![]() |
Definitie specificatori proprietate
Definitie specificator read
![]() |
Definitie specificator write
![]() |
Definitie specificator de stocare
![]() |
Definitie specificator implicit
![]() |
Definitie Câmp / Metoda
![]() |
Modul de utilizare al sintaxei de mai sus poate fi urmarit si din exemplul prezentat în continuare, care ne arata modul de scriere a codului sursa al unei componente în Delphi.
unit Exemplu;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs;
type
TArrayProp = array [1..5] of string[7];
TEverything = class(TCustomControl)
private
FArrayProp : TArrayProp;
function GetArrayPropInt(pIndex: integer): string;
function GetArrayPropStr(pIndex: string): integer;
protected
public
constructor Create(AOwner: TComponent); override;
property ArrayPropInt[Index: integer]: string read GetArrayPropInt;
property ArrayPropStr[Index: string]: integer read GetArrayPropStr;
published
end;
procedure Register;
implementation
constructor TEverything.Create(AOwner: TComponent);
begin
// Initializare FArrayProp
FArrayProp[1] := 'one';
FArrayProp[2] := 'two';
FArrayProp[3] := 'three';
FArrayProp[4] := 'four';
FArrayProp[5] := 'five';
end
function TEverything.GetArrayPropInt(pIndex: integer): string;
begin
result := 'Necunoscut';
// Daca pIndex este definit în FArrayProp, seteaza variabila result la valoarea de //indice pIndex
if pIndex in [1..5] then
result := FArrayProp[pIndex];
end
function TEverything.GetArrayPropStr(pIndex: string): integer;
var
x : integer;
begin
result := -1;
for x := 1 to 5 do
if UpperCase(FArrayProp[x]) = UpperCase(pIndex) then
begin
result := x;
exit;
end;
end
procedure Register;
begin
RegisterComponents('UD3', [TEverything]);
end
end
3.2 Tratarea exceptiilor în Delphi
Printre caracteristicile principale ale mediului Delphi, una dintre cele mai importante este capacitatea de tratare a exceptiilor, prin care programatorul poate sa raspunda elegant la aparitia oricarei erori de executie, simplificând astfel si codul, ceea ce permite programatorului sa se concentreze asupra algoritmilor principali ai aplicatiei. Exceptiile în Delphi sunt similare celor folosite de C++, locul lui catch este preluat de except iar throw devine raise în Delphi. La fel ca în C++ exceptiile sunt manipulate în blocuri, care la rândul lor pot sa suporte incluziunea.
Blocuri de protectie
În Delphi, tratarea exceptiilor se face prin asa numitele blocuri de protectie, care pot executa, fie un cod de terminare pentru a nu fi compromisa sesiunea Windows, fie o secventa de cod care trateaza efectiv exceptia, caz în care executia programului continua normal.
Aceste blocuri de protectie sunt la rândul lor compuse din alte doua tipuri de blocuri, un bloc de garda, care, asa dupa cum îi spune numele este raspunzator de interceptarea exceptiei si un bloc de raspuns ce contine codul de tratare al exceptiei, sau codul corespunzator terminarii aplicatiei.
Structura de principiu a unui bloc de protectie în Delphi este:
try
finally
end;
sau
try
except
end;
În varianta cu finally executia decurge astfel:
Daca vreuna dintre instructiunile cuprinse între try si finally provoaca o exceptie, atunci sistemul preda controlul blocului de raspuns, cuprins între finally si end. Daca nu a aparut nici o exceptie, dupa epuizarea instructiunilor cuprinse între try si finally, se continua cu executia instructiunilor cuprinse între finally si end.
Altfel spus, în varianta cu finally avem la dispozitie un mecanism cu ajutorul caruia avem garantia ca se executa un cod de terminare corecta a unui tip de prelucrare, indiferent de exceptiile care pot apare. Prin acest mecanism se urmareste limitarea efectelor posibilelor exceptii la executia unui program Delphi.
Sa mai adaugam faptul ca, în varianta cu finally, dupa executia codului de terminare, daca a aparut o exceptie, aceasta îsi continua evolutia (nu este ridicata), putând fi interceptata si tratata într-o bucla try externa, iar daca aceasta bucla nu este gasita, putând provoca în cele din urma terminarea anormala a programului.
În varianta cu except executia decurge astfel:
Lista instructiunilor din blocul try se executa în ordine. Daca nu apare nici o eroare, blocul except este ignorat, continuându-se executia cu prima instructiune aflata dupa end. Daca a aparut o exceptie, controlul este dat celui mai interior handler de exceptie existent. Daca un astfel de handler de exceptie nu exista, atunci se va cauta un handler de exceptii în exterior, în alt bloc try-except, neterminat înca. Daca un astfel de handler nu este gasit, se continua astfel pâna la epuizarea blocurilor try-except, sau pâna la gasirea handler-ului de exceptie adecvat. Daca nu este gasit nici un handler, se genereaza un mesaj standard de eroare si executia este terminata anormal.
În sfârsit, sa mai adaugam precizarea ca, similar modului în care utilizam în C++ enuntul throw, în Object Pascal putem folosi enuntul raise pentru a genera o exceptie.
Delphi pune la dispozitia programatorului clase de exceptii, extrem de utile pentru a monitoriza sistematic aparitia exceptiilor uzuale ale unei aplicatii Delphi (împartie la zero, acces nepermis la memorie, eroare la crearea unui fosier, etc.).
Amanunte despre protocolul de utilizare si detaliile de sintaxa pot fi urmarite si în exemplele de mai jos.
unit UExcept1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls;
type
TForm1 = class(TForm)
Edit1: TEdit;
Label1: TLabel;
Edit2: TEdit;
Label2: TLabel;
Button1: TButton;
Label3: TLabel;
Edit3: TEdit;
procedure Button1Click(Sender: TObject);
private
public
end;
var
Form1: TForm1;
Op1,Op2,Rezultat:real;
implementation
procedure TForm1.Button1Click(Sender: TObject);
begin
try
try
Op1:=StrToFloat(Edit1.Text);
Op2:=StrToFloat(Edit2.Text);
Rezultat:=Op1/Op2;
except
on EConvertError do
ShowMessage('Operanzi eronati...');
end;
Edit3.Text:=FloatToStr(Rezultat);
Edit1.SetFocus;
Edit1.Text:='';
Edit2.Text:='';
except
on EZeroDivide do
ShowMessage('Impartire la zero...');
end;
end
end
unit UEcept2;
//Aplicatie la tratarea exceptiilor in Delphi
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, Buttons;
// Codul care urmeaza este o interfata intre
//exceptiile aplicatiilor utilizator si sistem
// asigurata de clasa Exception din unit-ul SYSUTILS, prezentata mai jos sub forma de //comentariu.
(*Exception = class(TObject)
private
FMessage: string;
FHelpContext: Integer;
public
constructor Create(const Msg: string);
constructor CreateFmt(const Msg: string; const Args: array of const);
constructor CreateRes(Ident: integer);
constructor CreateResFmt(Ident: integer; const Args: array of const);
constructor CreateHelp(const Msg: string; AHelpContext: integer);
constructor CreateFmtHelp(const Msg: string; const Args: array of const;
AHelpContext: integer);
constructor CreateResHelp(Ident: integer; AHelpContext: integer);
constructor CreateResFmtHelp(Ident: integer; const Args: array of const;
AHelpContext: integer);
property HelpContext: integer read FHelpContext write FHelpContext;
property Message: string read FMessage write FMessage;
end;
type
EMyComponentError = class(Exception)
private
FErrorCode: integer;
public
property ErrorCode: integer read FErrorCode write FErrorCode;
constructor Create(const Msg: string; ErrCode: integer);
end;
EMyCompoRangeError =class(EMyComponentError);
EMyCompoInvalidValue =class(EMyComponentError);
TForm1 =class(TForm)
Button1: TButton;
Button3: TButton;
Button2: TButton;
Button4: TButton;
Button5: TButton;
ListBox1: TListBox;
Button6: TButton;
Button7: TButton;
Button8: TButton;
BitBtn1: TBitBtn;
procedure Button1Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button4Click(Sender: TObject);
procedure Button5Click(Sender: TObject);
procedure Button6Click(Sender: TObject);
procedure Button7Click(Sender: TObject);
procedure Button8Click(Sender: TObject);
procedure BitBtn1Click(Sender: TObject);
private
public
end;
var
Form1 :TForm1;
Stream :TMemoryStream;
implementation
// Rutinele care urmeaza sunt scrise utilizand
// maniera Delphi de tratare a exceptiilor
function CharFromString(sVal: string; iPos: integer): char;
begin
if iPos > Length(sVal) then
Raise Exception.Create('Out of Range');
result := sVal[iPos]
end
procedure InsertChar(cVal: char; var sVal: string; iPos: integer);
begin
if iPos > Length(sVal) then
Raise Exception.Create('Out of Range');
sVal[iPos] := cVal;
end
procedure UseFunctions;
var cVal: char;
sStrVal: string;
begin
try
sStrVal := 'Delphi Rock ';
cVal := CharFromString(sStrVal,13);
if cVal <> 's' then
InsertChar('s',sStrVal,12);
ShowMessage(sStrVal);
except
exit;
end;
end
procedure TForm1.Button1Click(Sender: TObject);
begin
UseFunctions;
end
// Aceste rutine rezolva aceleasi probleme ca
// mai sus intr-un stil de programare defensiv
// clasic.
function CharFromString(sVal: string; iPos: integer): char;
begin
Result := #0;
if iPos <= Length(sVal) then
result := sVal[iPos]
end
function InsertChar(cVal: char; var sVal: string; iPos: integer): boolean;
begin
Result := False;
if iPos <= Length(sVal) then
begin
sVal[iPos] := cVal;
Result := True;
end;
end
procedure UseFunctions;
var cVal: char;
sStrVal: string;
begin
sStrVal := 'Delphi Rock ';
cVal := CharFromString(sStrVal,12);
if cVal <> #0 then
begin
if cVal <> 's' then
if InsertChar('s',sStrVal,12) then
ShowMessage(sStrVal)
else
exit;
end
else
exit;
end
// Aceste rutine exemplifica utilizarea optionala
// a sintaxei [on..do] a unei constructii [try..except]
procedure LowMemTerminate;
begin
Application.Terminate;
end
procedure TForm1.Button2Click(Sender: TObject);
var pVal: PChar;
begin
try
GetMem(pVal,sizeof(Stream.Memory^));
StrLCopy(pVal,Stream.Memory,sizeof(Stream.Memory^)+1);
except
On E: EOutOfMemory do
LowMemTerminate;
On E: EAccessViolation do
begin
if MessageDlg(E.Message+': Terminate the program?',
mtError,[mbYes,mbNo],0) = mrYes then
end
else
Raise;
end;
end
procedure TForm1.Button3Click(Sender: TObject);
var pVal: PChar;
begin
try
GetMem(pVal,sizeof(Stream.Memory^));
StrLCopy(pVal,Stream.Memory,sizeof(Stream.Memory^)+1);
except
On E: Exception do
begin
if E is EOutOfMemory then
LowMemTerminate;
if E is EAccessViolation then
begin
if MessageDlg(E.Message+': Terminate the program?',
mtError,[mbYes,mbNo],0) = mrYes then
Application.Terminate;
end
else
Raise;
end;
end;
end
// Imbricare constructii [try..except] si [try..finally]
procedure TForm1.Button4Click(Sender: TObject);
var sVar: string;
pVar: PChar;
begin
try
GetMem(pVar,10);
try
StrCopy(pVar,'ListBox');
with ListBox1.Items do
begin
Add('Line1 - index 0');
Add('Line2 - index 1');
end;
sVar := ListBox1.Items[2];
finally
FreeMem(pVar,10);
end;
except
on EStringListError do
MessageDlg('List index out of bounds', mtError, [mbOk], 0);
on EAccessViolation do
MessageDlg('Memory Overwrite', mtError, [mbOk], 0);
else
Raise;
end;
end
procedure TForm1.Button5Click(Sender: TObject);
var sVar: string;
pVar: PChar;
begin
GetMem(pVar,10);
try
StrCopy(pVar,'ListBox');
try
sVar := ListBox1.Items[0];
except
on EStringListError do
begin
ListBox1.Items.Add('Item1');
sVar := ListBox1.Items[0];
end
else Raise;
end;
finally
FreeMem(pVar,10);
end;
end
// Aceste rutine arata utilizarea unei instantede tip EAbort
// [Silent Exception]
var Delphi: string;
procedure SetDelphiVariable(sValue: string);
begin
if Delphi = sValue then
Raise EAbort.Create('');
if (Length(sValue) > 0) and (Length(sValue) < 50) then
Delphi := sValue;
end
procedure TForm1.Button6Click(Sender: TObject);
begin
SetDelphiVariable('Anders Hejlsberg');
end
// These routines demonstrate the use of raising an
// exception of type EAbort - the silent exception
constructor EMyComponentError.Create(const Msg: string; ErrCode: integer);
begin
Inherited Create(Msg);
FErrorCode := ErrCode;
end
const
cInvalidValue = 0;
cRangeError = 1;
// These routines relate to the creation of custom exception
// types. Move statments into your global handler and
// replace "Sender" with EInstance to test
procedure TForm1.Button7Click(Sender: TObject);
begin
if Sender is EMyComponentError then
case (Sender as TCOmponent).Tag of
cInvalidValue: ;
cRangeError : ;
else
begin
ShowMessage('Fatal Error - Terminating program');
Application.Terminate;
end;
end;
end
procedure TForm1.Button8Click(Sender: TObject);
begin
if Sender is EMyComponentError then
begin
if Sender is EMyCompoRangeError then
else
if Sender is EMyCompoInvalidValue then
;
end
else
begin
ShowMessage('Fatal Error - Terminating program');
Application.Terminate;
end;
end
procedure TForm1.BitBtn1Click(Sender: TObject);
begin
Close
end;
end.
|