Delphi Object Pascal pro začátečníky

vložil Radek Červinka 18. května 2010 00:15

Pořád cítím, že bych měl něco napsat i pro začátečníky, kde bych osvětlil některé věci ohledně Object Pascalu, když už se na jejich znalost spoléhám.

A tak jsem se podíval do svého archívu a spojil několik útržků textů, které jsem kdysi napsal z jiných důvodů a výsledek trošku upravil. Nejedná se o učebnici, ani o kompletní přehled, ale spíše takové základy a postřehy. Pro pochopení předpokládám aspoň základní znalosti Pascalu.

Od myšlenky k Delphi

V jedné knížce o Delphi jsem četl, že když se kdysi u Borlandů přemýšlelo o první verzi Delphi, tak někdo při brainstormingu napsal na tabuli

Button.Caption:='text'

a tím byl základ Delphi položen.

Když totiž například napíšeme uvedený kód, tak to není jen prosté uložení řetězce, ale zároveň to způsobí překreslení popisky tlačítka. Je to umožněno tím, že pro zápis je definována metoda (viz dále, ale pro neznalé procedura třídy), která toto chování implementuje. Kompilátor při kompilaci místo prostého přiřazení provede volání zápisové metody.

Ale při čtení titulku (Caption) už nic kreslit nemusíme, takže může probíhat např. přímo z interní proměnné třídy. Při čtení tedy není žádná časová ztráta. Kompilátor tedy provede např. jen přiřazení. Caption je property objektu Button (tlačítko).

Property tedy umožňuje provádět různé akce při čtení a zápisu dat, sice se k property ještě dostaneme, ale prozatím: Property je jedním z klíčových prvků při programování v Object Pascalu, i když si to třeba neuvědomujeme.

Třída a objekt

Programátoři často chybují v pojmech třída a objekt. Oblíbeným rčením je, že objekt je instance třídy.

Nejjednodušší je uvést analogii mezi typem proměnné a proměnnou. Třída je jakoby typ a objekt je jakoby proměnná. Ve své podstatě typ a třída existuje jen jako pomůcka pro programátora a kompilátor.

Velikost vlastního objektu je v 32bit kompilátoru 32bitů, tj. 4 byte a ve skutečnosti se jedná o ukazatel (pointer) ukazující na data objektu v paměti.

A zde máme druhý důsledek tehdejšího brainstormingu: jestliže je objekt vlastně pointer, tak by se logicky mělo psát:

  Button^.Caption:='text'

Kompilátor ale při zjištění, že se odkazujeme na objekt dereferenci (^) odpustí.

Pokud tedy napíšeme

var objekt:TMujObject

, tak je sice v paměti vymezen prostor pro objekt (= pointer = u 32 bitového překladače 4 bajty), ale objekt ještě neexistuje. Teprve voláním konstruktoru (dle konvence Create, ale není to předpis, navíc může být konstruktorů více) je v paměti na haldě alokováno místo o potřebné velikosti.

Destruktor (dle konvence Destroy) objekt z paměti uvolní a paměť je vrácena. Pozn.: Místo Destroy volejte metodu Free, která interně destruktor zavolá. Objekt je sice uvolněn, ale proměnná pořád obsahuje odkaz na (nyní již naplatnou) paměť. Lepší je proto volat proceduru FreeAndNil, která má parametr typu TObject a po zavolání Free nastaví předaný parametr na nil, což značí prázdný objekt.

    1FreeAndNil(objekt);

FreeAndNil testuje zda není objekt nil, není třeba testovat předem manuálně.

Základní třídou je TObject. Třída kromě jiného zavádí constructor Create a destructor Destroy a několik dalších metod. Mezi ně patří i již uvedená metoda Free, která interně volá destruktor a uvolňuje vytvořený objekt. Konstruktor vytváří instanci třídy, tj. vytvoří objekt. Destruktor objekt uvolní.

Deklarace třídy může vypadat takto

    1interface
    2type
    3  TMyClass=Class (TObject) // když nic, tak to přesto značí TObject
    4  protected
    5    fidata: Integer; 
    6    procedure mSetData(iValue:Integer); 
    7  public
    8    constructor Create; // náš konstruktor
    9    destructor Destroy; override; // náš destruktor
   10    property piData:integer read fidata write mSetData; 
   11  end;
   12  
   13implementation
   14constructor TMyClass.Create;
   15begin
   16  // zde vytvoříme, vše co potřebujeme 
   17end;
   18
   19procedure TMyClass.mSetData(iValue:Integer);
   20begin
   21  fidata: iValue;
   22end;
   23    
   24destructor TMyClass.Destroy;
   25begin
   26  // provedeme potřebné akce (uvolnění zdrojů, které jsme vytvořili)
   27  inherited //zavoláme předchůdce (pokud je to třeba)
   28end;

Zde bych se rád trošku pozastavil u správy paměti Delphi. Jelikož jsou často alokovány malé bloky paměti, bylo by neefektivní, aby tím byl neustále obtěžován správce paměti v systému. Místo toho si Delphi alokuje větší bloky paměti, a z něho vlastním správcem paměti přiděluje malé kousky. Až paměť dojde je vyžádán další kus. Na konci běhu jsou všechny bloky paměti vráceny zpět systému. Při použití FastMM (nebo Delphi 2005+) je algoritmus přidělování komplikovanější a efektivnější a už jsem o něm zde psal.

Pokud zapomeneme zavolat konstruktor, tak objekt není vytvořen. Jelikož si ale kompilátor nemůže být jist, že to tak nechceme je zahlášen pouze warning. Případné volání nějaké metody skončí s chybou přístupu k paměti a je vyvolána výjimka EAccessViolation. Tato, stejně jako ostatní výjimky je následníkem třídy Exception. Pokud není výjimka odchycena programátorem (viz. dále) je odchycena globální obsluhou výjímek a je zobrazeno varování, ale program není ukončen. Často je tento globální ovladač nahrazen lepším (třeba z JCL), který zobrazuje třeba výpis zásobníku při chybě atd.

Viditelnosti

Od počátku programování je zjevná snaha o modularitu a případně skrytí implementačních detailů a zpřístupnění pouze rozhraní. Z hlediska historie pascalu byla jedním z prvním vývojových stupňů jednotka (unit) s dvěma stupni viditelnosti (interface x implementation). V Object Pascalu máme pro každou třídu možnost definovat několik stupňů viditelnosti.

    1TMyClass=Class(TObject)
    2  private
    3  protected
    4  public
    5  published
    6  strict private // delphi 2006+
    7  strict protected // delphi 2006+   
    8end;

V každé z těchto sekcí může být uvedena deklarace metody, proměnné (někdo tomu říká i jinak) nebo property (má hlavně význam u public a published).

Sekce private (strict private) je nejpřísnější. Co je zde uvedeno, tak není vidět nikde kromě jednotky, kde je třída umístěna. Píši schválně jednotky a ne třídy, jelikož dvě třídy v jedné jednotce si mohou navzájem přistupovat k polím v části private (neplatí pro strict private – tam se opravdu jedná jen o třídu).

Sekce protected (strict protected) viditelnost lehce uvolňuje, jelikož pole zde definovaná lze vidět i v následnících mateřské třídy, popř. ve stejné jednotce. V ostatních případech pole vidět nejsou. Analogicky k strict private je u strict protected omezení na pouze následníci.

Pole v sekci public jsou vidět všude.

Pole v sekci published mají stejnou viditelnost jako public. Rozdíl je, že za běhu se vytvářejí RTTI (Run Time Type Information). Díky tomu mohou externí aplikace (jako debugger Delphi) získat za běhu o těchto polích doplňující informace. RTTI bylo v Delphi 2010 významně rozšířeno.

Metody

Metody poskytují rozhraní pro přístup k proměnným třídy, popř. pracují nad proměnnými třídy.

Jelikož někde používám výraz procedura, někde funkce a někde metoda tak na vysvětlení: Funkce se liší od procedury tím, že vrací hodnotu (hodnota je v těle funkce reprezentována proměnnou Result stejného typu jako je návratová hodnota). Metoda je "procedura" nebo "funkce" u třídy. U nových Delphi lze návratovou hodnotu specifikovat jako parametr Exit, např. Exit 20.

Pozn: Klasickým názorem je, že OOP je nutně pomalejší než klasické programování. Když se oprostíme od již uvedených výhod OOP, tak bych rád upozornil, že většina metod nemá v podstatě parametry (jejich data jsou z převážné většiny součástí definice třídy) a tudíž volání by mělo být teoreticky (i prakticky) rychlejší. Jen je třeba myslet na to, že se předá vždy jeden parametr navíc – odkaz na objekt, tj. Self.

V Delphi jsou standardně první tři parametry předávány v registrech procesoru. Pokud specifikujeme za deklarací metody klíčové slovo inline, Delphi zváží možnost začlenění volané metody jako přímý kód, tj. bez volání.

Speciálním typem metody (resp. to není úplně metoda) je class helper, kterému byl věnován speciální článek.

Metody statické

Metody statické jsou všechny metody, pokud není uvedeno jinak. Pokud kompilátor narazí na kód typu Třída.StatickáMetoda, tak prostě vygeneruje kód pro volání StatickáMetoda v objektu Třída nebo pro volání této metody u předchůdce, od kterého tuto metodu podědila.

Metody virtuální

Metody virtuální jsou označovány v deklaraci třídy jako virtual.

procedure Test5; virtual;

Na rozdíl od statických metod kompilátor negeneruje kód přímo pro volání konkrétní třídy, ale používá se mechanismu pozdního svázání. Metoda, která se bude volat, je určena až za běhu.

Virtuální metoda může být v následnících předefinována (override). Při předefinování musí být deklarace přesně zachována (mění se pouze implementace).

  procedure Test5; override;

Pokud tedy kompilátor narazí na konstrukci Třída.VirtuálníMetoda tak je možné, že byla metoda předefinována. Vygenerovaný kód musí za běhu nalézt v tabulce virtuálních metod správnou metodu a zavolat ji.

Metody dynamické

Metody dynamické se používají ke stejnému účelu jako virtuální. Jsou označovány v deklaraci třídy jako dynamic. V praxi se používají minimálně.

  procedure Test5; dynamic;

Rozdílem je, že volání dynamické procedury jsou pomalejší, ale zabírají méně prostoru. Používají se v případě, že základní třída má mnoho metod, které se sice mohou předefinovat, ale neděje se tak příliš často a navíc má třída mnoho následníků.

Metody abstraktní

Metoda je definovaná, ale nemá implementaci. Instance této třídy způsobí výjimku jen v případě, že se na ní za běhu narazí (na rozdíl od Javy a C# jde přeložit, i když s warningem). Metoda musí být v následnících předefinována, a proto musí být virtuální.

  procedure Test5; virtual; abstract;

Speciální deklarace metod a funkcí

Pokud chceme definovat několik metod (nebo např. konstruktorů) se stejným názvem, musíme tuto skutečnost kompilátoru sdělit. Tímto kouzelným slůvkem je overload. Každá z metod pak musí mít svoji implementaci.

  procedure Test6 (aValue:String); overload;
  procedure Test6 (aValue:Integer); overload;

Konstantní parametry

Nejsem si jist, zda je to korektní pojmenování, ale jednak můžeme specifikovat, že parametr se nebude v metodě měnit (a tím a) napovíme kompilátoru, b) se pojistíme proti případné chybě), druhak můžeme říct, že procedura bude mít další parametr, ale pokud nebude při volání specifikován, bude použita uvedená hodnota.

  procedure Test7 (const AValue:string; const AVal2:String = “”);

Volání předchůdce třídy

Předchůdce zavoláme pomocí klíčového slova inherited.

Destructor TMyClass.Destroy;
begin
  inherited;
// nebo taky inherited Destroy;  
end;

Přetypování

Třídu můžeme přetypovat použitím klíčového slova AS. Přetypování bychom měli použít jen tehdy, když si jsme jisti, že daný objekt obsahuje instanci (nebo následníka) třídy, na kterou ho chceme přetypovat. Pokud to není pravda je vyvolána výjimka EInvalidCast.

Explicitní přetypování je taky možno způsobem Třída(Objekt). V tomto případě ale není zaručeno, že nedojde k chybě přetypováním (není kontrolován typ objektu), ale někdy je to výhodné.

Operátor IS slouží k určení, zda je typ daného objektu roven danému typu třídy nebo některému následníku. Například if Button1 IS TCheckBox then ….

Dědičnost

Pokud definujeme, že nějaká třída je následníkem jiné třídy, tak to kromě jiného znamená, že nová třída získá všechny metody a proměnné, které byly definovány pro předchůdce (s ohledem na viditelnost).

Vlastní operace je zapsána takto:

type
  TNaslednik = class (TPredchudce)
  // zde přidáme nové 
  end;

Předchůdce zavoláme pomocí inherited. Klíčové slovo Self je referencí na sebe sama (např. self.Print) a interně je kompilátorem předáváno jako první parametr při volání.

Polymorfismus

Polymorfismus je jedna ze základních vlastností OOP. Raději rovnou uvedu klasický příklad se zvířaty:

    1unit uObjects;
    2
    3interface
    4type
    5  TZvire=class
    6    function Zvuk:String;virtual;
    7    class function Jmeno:string; virtual;
    8  end;
    9
   10  TPes=class (TZvire)
   11    function Zvuk:String;override;
   12    class function Jmeno:string; overide;
   13  end;
   14		
   15implementation
   16		
   17function TZvire.Zvuk:String;
   18begin
   19// obecný zvuk roztomilého zvířátka 
   20  Result:='grrrrrrrr';
   21end;
   22
   23class function TZvire.Jmeno:String;
   24begin
   25  Result:='zvíře';
   26end;
   27		  
   28class function TPes.Jmeno:String;
   29begin
   30  Result:='pes'; 
   31end;
   32		  
   33function TPes.Zvuk:String;
   34begin
   35  Result:='hafinky'; //pes je taky zvíře
   36end;
   37    
   38end.

V uvedeném unitu jsou definovány dvě třídy: TZvire a TPes. Třída TPes je následníkem TZvire a je v ní předefinována jedna metoda.

Hlavní program (uložen v TestOOP.dpr):

    1program TestOOP;
    2uses
    3  Classes,
    4  uObjects in 'uObjects.pas';
    5    
    6var
    7  zvire:TZvire;
    8  pes:TPes;
    9  
   10procedure ProjevSe(const Kdo:String; AZvire:TZvire);
   11begin
   12 writeln(Kdo + ' dělá ' + AZvire.Zvuk);
   13end;
   14	   
   15begin
   16  zvire:=TZvire.Create; // instance zvířete
   17  pes:=TPes.Create;  // instance psa
   18  try
   19    ProjevSe(TZvire.Jmeno, zvire);
   20    ProjevSe(TPes.Jmeno, pes);
   21  finally
   22    pes.Free;
   23    zvire.Free;
   24  end;
   25end.

Jádro příkladu je druhý parametr procedury (nikoliv metody - viz výše).

Všimněte si, že je typu TZvire a přesto ji v hlavním programu volám i s objektem pes. Metoda Zvuk je virtuální a proto je za běhu zavolána ta metoda, která odpovídá objektu.

Navíc je zde ukázána class function, tj. funkce, která patří k třídě a ne objektu, a dá se tedy volat i nad třídou. Podrobněji to bylo již na našem serveru probíráno, včetně dalších možností.

Výsledkem běhu programu bude:

Zvíře dělá grrrrrrrr
Pes dělá hafinky

Komponenta a formulář

Komponenta

Komponenta je objekt s určitými vlastnostmi (jedná se o následníka TComponent - viz help). Pokud tedy vytvoříme následníka této třídy, tak se s ním bude dát manipulovat na formuláři a může být přidán do palety komponent. Dále může vlastnit jiné komponenty a v neposlední řadě bude umět zapsat a načíst svá data do a ze streamu (tj. proudu dat).

Formulář

Obecný formulář (TForm) je nepřímým následníkem TComponent. Pokud vytvoříme vlastní formulář – je to následník právě TForm.

Možnost čtení dat z a do streamu je velmi důležitá. Celý popis formuláře je složen právě z dat komponent a informací o tom, kdo kterou komponentu vlastní. Pokud máme např. na formuláři tlačítko, ve skutečnosti to znamená, že formulář má mezi podřízenými komponentami jedno tlačítko.

Pokud tedy například pohneme tlačítkem doleva, tak se u dané komponenty změní property Left. Při ukládání komponenty (do souboru *.dfm) je hodnota Left zapsaná (tedy pokud není aktuální hodnota hodnotou defaultní). Během fáze kompilace (resp. linkování) jsou dfm soubory vloženy do výsledné binárky (tj. do EXE).

Soubor *.dfm tedy obsahuje textový popis komponent na formuláři a je generovaný IDE. Zobrazení popisu formuláře, tj. obsahu souboru dfm, lze provést v IDE při zobrazeném formuláři např. ALT+F12 (a zase zpět).

Při kompilaci se textový popis převede na binární a přilinkuje se k aplikaci. Při vytváření formuláře za běhu, je vytvořena instance formuláře, a pak postupně všech dalších komponent, které na formulář patří. Při jejich vytváření jsou ze spustitelného souboru nataženy uložené vlastnosti (každá komponenta svoje, tedy tlačítko i to svoje Left). No a jelikož je Left property dojde (skrze zápisovou metodu) k posunutí komponenty na správné místo určené při návrhu.

Ukážeme si popis formuláře s jednou komponentou TMemo. Formulář má jméno frmMain a komponenta Editor. Formulář je vlastníkem Editoru. Popis formuláře v souboru fMain.dfm:

object frmMain: TfrmMain
  Left = 200
  Top = 157
  Width = 783
  Height = 540
  ActiveControl = Editor
  Caption = 'Hlavní okno'
  Color = clBackground
  PixelsPerInch = 75
  TextHeight = 16
  TextWidth = 7
  object Editor: TMemo
    Left = 0
    Top = 0
    Width = 783
    Height = 540
    Align = alClient
    TabOrder = 0
  end
end

a třída formuláře v fMain.pas:

    1unit fMain;
    2interface
    3
    4type
    5  TfrmMain = class(TForm)
    6    Editor: TMemo;
    7  end;
    8var
    9  frmMain:TfrmMain;
   10  
   11implementation
   12
   13 {$R *.dfm}  // přilinkujeme výše uvedený dfm soubor
   14end.

Teď se určitě ptáte, kdo vytváří formuláře. Při pohledu do souboru dpr (menu Project/View Source) uvidíme následující:

    1program Test1;
    2
    3uses
    4  Forms,
    5  fMain in 'fMain.pas' {frmMain};
    6   
    7begin
    8  Application.Initialize;   // inicializace objektu Application
    9  Application.MainFormOnTaskbar := True;
   10  Application.CreateForm(TfrmMain, frmMain); // vytvoření formuláře
   11  Application.Run;         // jdeme na to
   12end.

V případě více formulářů se řádek s CreateForm opakuje (samozřejmě s jinými parametry).

Pokud nechceme při startu aplikace vytvářet všechny formuláře (a to určitě nechceme, protože to u složitějších formulářů chvilku trvá a zbytečně to zabírá paměť), můžeme tyto řádky smazat. Druhou možností je říct IDE, že si to nepřejeme (menu Project/Options/Forms). V tom případě musíme vytvářet formuláře sami v okamžiku potřeby. První vytvořený formulář se stane hlavním formulářem aplikace. Pokud se tedy po startu zobrazuje jiné okno než je Vaše ctěná libost - zkontrolujte pořadí vytváření formulářů.

Visual Form Inheritance

Velmi vhodné je používat Visual Form Inheritance. Pod tímto tajuplným názvem se skrývá dědění formulářů (opakuji – formulář je také objekt, takže se dá dědit).

Představme si, že máme aplikaci, kde máme deset oken, které se liší pouze v detailech. Jestliže pro všechny okna nalezneme společné prvky (a metody) je výhodné použít dědění formulářů.

Společné prvky budou tvořit předchůdce. Těchto předchůdců může být i více a mohou být od sebe poděděny. Raději proto uvedu příklad: Mějme formulář (nazvěme ho třeba TIniForm) jehož jediným úkolem bude při svém vytvoření načíst informace o své pozici a velikosti z ini souboru a při rušení je tam zase uložit. Tento formulář je následníkem standardního TForm. Nyní mějme další formulář, který nazveme třeba TBasicForm. Tento formulář bude obsahovat třeba nějaká tlačítka (OK, Cancel …) a jejich obsluhu, ale jelikož je následníkem TIniForm tak bude umět číst a zapisovat svoji konfiguraci do ini. No a konečně mějme formulář TMainForm, který bude následníkem TBasicForm. Bude umět vše co předchůdce a navíc tam přidáme požadované prvky.

Výhod je při dobrém návrhu několik:

  • Všechny změny v předchůdcích se automaticky promítají do následníků (zákazník chce konfiguraci oken v XML? No problem, prostě změníme naše dvě metody v TIniForm).
  • Neduplikují se nám zdroje (bitmapy, komponenty …) tudíž je aplikace menší.
  • Lehčeji udržovatelný kód (změnil jsem to ve všech oknech, nebo nezměnil??).
  • Atd.

Procedurální typ

Jelikož je Object Pascal přísně typovým jazykem existuje také typ procedura (a v novějších verzích třeba i anonymní metody). Ve skutečnosti je to v podstatě ukazatel na proceduru nebo funkci.

Tím, že zadefinujeme tento typ, získáme možnost bezpečně přiřazovat nebo předávat odkaz a kompilátor nikdy neztratí kontrolu nad parametry, čímž minimalizujeme možnost předání špatného typu (nebo počtu) parametru. Použil jsem to např. v článku o DLL.

type
  TMyProc = function (x,y:Integer):Integer;
  TMyMethod = function (x,y:Integer):Integer of Object;

První deklarace definuje funkci s dvěma parametry a návratovým typem Integer. Druhá deklarace definuje metodu se stejnou syntaxí.

    1function Test(a,b:Integer):Integer;
    2begin
    3  Result:=a+b
    4end;
    5
    6var 
    7  mp:TMyProc;
    8begin
    9  mp:=Test;
   10  writeln(mp(1,2)); 
   11end;

Dědičnost rozhraní

Object Pascal (stejně jako třeba C#) neumí vícenásobnou dědičnost, ale jen vícenásobnou dědičnost rozhraní, anglicky Interface (u nás na Moravě někdy jako meziksicht). První předchůdce musí být následníkem TObject. Vhodným objektem je TInterfacedObject, který má implementované metody pro počítání referencí. Následně je uveden seznam rozhraní, které je nutno implementovat.

    1type
    2  IRozhrani1=Interface
    3    procedure Metoda;
    4  end;
    5       
    6  TMyObject=class(TInterfacedObject, IRozhrani1)
    7    procedure Metoda;
    8  end;
    9implementation
   10  
   11procedure TMyObject.Metoda;
   12begin
   13	       
   14end;

Podmíněná kompilace

Někdy je třeba umožnit kompilaci rozdílných verzí programu podle požadovaných podmínek. Např. vytváříme shareware, tak při startu necháme zobrazovat informaci o tom shareware, ale chceme mít i možnost pro platící vytvořit verzi bez informace. Rozlišení provedeme pomocí podmíněné kompilace. Parametry pro ni se dají specifikovat jak v IDE, tak třeba v kódu.

Níže třeba společný kód pro Kylix a Delphi.

 
uses
 {$IFDEF LINUX}
   QForm, QMenus, QGraphics …,
 {$ELSE}
   Form, Menus, Graphics …,
 {$ENDIF}
 Classes, SysUtils …

Pro více informací nahlédněte do nápovědy.

Strukturovaná obsluha výjimek

Kromě property považuji za další méně známý stavební prvek Delphi strukturovanou obsluhu výjimek. V podstatě se jedná o konstrukce try..except, try..finally a raise. První konstrukce odchytí výjimku, druhá zabezpečí provedení kódu jak v případě bez chyby tak s chybou a raise vyhodí vyjímku (případně poslední výjimku - viz příklad).

Mějte na paměti, že na rozdíl od klasického programování třeba v C, kdy programátor jako trubka by měl důsledně testovat návratové hodnoty jednotlivých volání funkcí (třeba otevření souboru, test alokace paměti atd), v Delphi prostě napíšeme kód tak, jak by měl v ideálním případě proběhnout a pouze tento kód obalíme obsluhou výjimek, které očekáváme a zbytek necháme na globálním ošetření, třeba takto (všimněte si zanoření finally a except):

    1procedure TForm1.btn1Click(Sender: TObject);
    2var
    3  sl: TStringList;
    4const
    5  csFileName = 'data.txt';
    6begin
    7  try
    8    sl := TStringList.Create;
    9    try
   10      sl.LoadFromFile(csFileName);
   11      ShowMessage(sl.Text);
   12    finally
   13      FreeAndNil(sl);
   14    end;
   15  except
   16    on E: EFOpenError do
   17    begin
   18      ShowMessage(Format('Soubor [%s] nenalezen!', [csFileName]))
   19    end
   20    else
   21      raise
   22  end;
   23end;

Ať se stane co se stane, tak sekcí finally to projde vždy. Toto je duležité, jelikož i něco jako:

try
   blabla
   if něco then exit;
finally
   // se provede i při exit
end;

A poslední poznámka: ohledně umístění try…finally – try až za vytvořením objektu.

o := TStringList.Create;
try
finally
   FreeAndNil(o);
end;

Příkaz With

Mnoho programátorů nemá příkaz with rádo, a postupem časem se k nim čím dál více řadím. Přesto některé konstrukce jsou celkem populární:

with TMyInputDialog.Create(nil) do
begin
  try
    if ModalResult <>  mrOK then Exit;
  finally 
    Free;
  end;
end;

nebo pokud pracujeme např. s jedním záznamem nebo objektem, tak si ušetříme dlouhé vypisování (a speciálně u složitých property i můžeme za použití with zrychlit kód, ale obecně se vyplatí spíše myslet na budoucnost a nahradit with lokální proménnou). Dokonce můžeme provést with objekt1, objekt2 do, ale to už musíme být velmi opatrní a raději bych to nedělal.

Někdy třeba používám tuto konstrukci

with ds.FieldByName(‘Name’) do
begin
    if AsString = OldValue then
       AsString := ‘xxxx’;
end;

kde FieldByName vrátí objekt, nad kterým se dále pracuje.

Častý argument (se kterým absolutně souhlasím) proti with je, že změna v rozhraní (přidání/odebrání/přejmenování vlastnosti) může způsobit že kód se zkompiluje, ale bude dělat něco jiného, resp. bude přistupovat k vlastnostem jiného objektu.


Nabízíme Delphi školení a konzultace na různá témata, primárně ve Vaší firmě.

Tagy: , , ,

Začátečníci

Komentáře

21.5.2010 14:42:34 #

JaroB

Rád bych podotknul ke konstukci "width - do", že v Delphi 2005 je chybka, která chybně vyhodnocuje letmo vytvořené objekty v následujícím bloku (při zakrytí metod/property). V tomto případě se musí udělat lokální proměnná, čímž se chyba obejde.

JaroB

Komentování ukončeno

Naše nabídka

Partial English version.

MVP
Ing. Radek Červinka - Embarcadero MVP
profil na linkedin, Twitter:@delphicz

Nabízím placené poradenství a konzultace v oblasti programování a vývoje SW.
Dále nabízíme i vývoj speciálního software na zakázku.

Neváhejte nás kontaktovat (i ohledně reklamy nebo burzy práce).

Pokud chcete podpořit tento server libovolnou částkou, můžete použít PayPal. Moc děkuji.

Delphi Certified Developer

O Delphi.cz

Delphi je jediný moderní RAD nástroj podporující tvorbu nativních aplikací pro platformu Win32, Win64 , Mac OSX a na iPhone a Android (s výhledem na další platformy díky FireMonkey) na současném trhu (včetně Windows 8.1).

V současnosti je světová komunita přes dva miliónů vývojářů.

Delphi.cz je nezávislý portál pro uživatele Delphi. Portál není koncipován pro úplné začátečníky, i když i ti se zde nebudou nudit, ale spíše na programátory, kteří již něco znají a chtějí své znalosti dále rozvíjet a sledovat novinky.

Anketa

Poslední komentáře

Comment RSS