A Delphi nem követeli meg a modulokra bontást, ennek ellenére gazdag eszközkészletet ad a modularizációhoz, ami a grafikus, eseményvezérelt programok készítéséhez elengedhetetlen. Alapértelmezésben modulokra bontott programot készít a Delphi. Kétféle modul van: főmodul (lehet program, library, package) és egység (unit).
Delphiben minden egységnek van neve (projectenként egyedi kell legyen), amely megegyezik az egységet tartalmazó forrásfájl nevével. Az egység támogatja a programozó-fordító szerződés modellt, mivel a fordító ellenőrzi a kód helyes felhasználását. Egy unit lefordítható tárgykód formátumra, amely a unit forráskódja nélkül is hozzáfűzhető más programok forráskódjához, így védi a forráskódot. Használata esetén csak a hivatkozott kódrészletek kerülnek bele a lefordított kódba, valamint az esetleges initialization – finalization szekciók kódjai.
Az egység általános szerkezete, az összes lehetséges részt beleértve, a következő:
Természetesen egy unitnak nem kell feltétlenül fenti elemek mindegyikét tartalmaznia. A minimális egység (az úgynevezett üres egység) a következő (új egység hozzáadásakor ilyen kódot generál a Delphi):
Minden egység implicit hivatkozik a System egységre. Az egységek nem hivatkozhatják körbe egymást az interface részben, viszont úgy már lehetséges a hivatkozás, ha a kör legalább egy tagja csak az implementation részben hivatkozik a következőre.
A const, type, var szekciók sorrendje tetszőleges lehet, viszont fontos a definiálási sorrend: csak már definiált névre lehet hivatkozni. Példa előzetes definiálásra:
A hivatkozott egységek minden exportált szimbóluma elérhető a hivatkozó egységben. Ha több egység exportál ugyanazzal a névvel szimbólumot, akkor minősített névvel különböztethetjük meg őket, pl. Unit1.MyType. Az újabb Delphi verziók lehetővé teszik az egységek „névtérbe” szervezését, ha az egység nevét pontokkal tagoljuk (pl. unit MyCompany.MyWidgets.MyUnit;). A névterek neve nem szimbólum, hanem az egység nevének része. Bizonyos esetekben az egységre való hivatkozásnál elhagyható a névtérrész.
Program: egy végrehajtható programhoz tartozó főmodul. Kiterjesztése általában .dpr. Egy program tartalmaz programfejlécet (program név), a hivatkozott egységek listáját (uses), tetszőleges számú deklarációt, végül a főprogramot begin ... end. között. A főprogramnak nincsenek paraméterei és visszatérési értéke. A parancssori paramétereket a ParamCount és ParamStr könyvtári függvényekkel érhetjük el, a program visszatérési értékét pedig a globális ExitCode változóban adhatjuk meg. A klasszikus „Hello, World!” alkalmazás objektumorientált változata a következőképpen néz ki:
Library: különálló fájlba fordítandó függvénykönyvtár (DLL). A DLL által exportált alprogramokat akár más nyelven írt programból is meghívhatjuk. A DLL modul tartalmaz egy fejlécet (library név), tetszés szerint uses részt, tetszőleges számú deklarációt, a végén pedig exports részt és tetszés szerint főprogramot, amely a könyvtár betöltésekor kerül végrehajtásra. Az exports részben felsoroljuk a könyvár által exportált alprogramokat. Mindegyikhez adhatunk egy álnevet (pl. exports DoSomethingABC name 'DoSomething';), ha ezt nem tesszük meg, akkor az eredeti nevével kerül exportálásra.
Package: a DLL-lel szemben ez egy kizárólag Delphiben használható könyvtár. Egy csomag Delphi egységeket tartalmaz, valamint megadhatók neki függőségek. Készíthetünk „tervezési idejű” (design-time) csomagot, amellyel a Delphi IDE képességeit bővíthetjük ki vagy a programokban felhasználható komponenseket valósíthatunk meg. A csomag főmoduljának kiterjesztése .dpk. Ha lefordítjuk, egy .bpl kiterjesztésű fájlt kapunk, amelyet a Delphi „Component -> Install Packages...” menüpontjával tölthetünk be az IDE-be. A programok által hivatkozott csomagokat a fordító statikusan köti a programhoz, de lehetőség van a dinamikus betöltésre is. (A programhoz csatolandó csomagokat a projekt tulajdonságaiban állíthatjuk be.)
A csomag főmodulja az alábbi elemeket tartalmazza:
A csomagok nem hivatkozhatják körbe egymást a requires részben. Egy csomag nem tartalmazhatja a függőségei által tartalmazott egységeket, és egy programhoz nem csatolható két olyan csomag, amely tartalmaz azonos egységet.
A Pascalban alprogram deklarálására kétféle lehetőség kívánkozik: az eljárás (procedure) és a függvény (function). Az egyetlen különbség köztük az, hogy a függvénynek van visszatérési értéke, míg az eljárásnak nincs. Pascal terminológiában az eljárást és a függvényt összefoglaló néven rutinnak nevezik. Lehetőség van az alprogramok egymásba ágyazására.
Az eljárások meghatározásának szintaxisa:
A visszatérő értéket a
vagy
alakban adhatjuk meg. Ezeket az értékadásokat több helyen is kiadhatjuk a függvénytörzsben. {$X+} állapotban a Result nevet felhasználhatjuk kifejezésekben a benne tárolt érték kiolvasására, viszont a függvénynév kifejezésben való felhasználása rekurzív függvényhívást idéz elő.
A Turbo Pascaltól eltérően a Delphiben a függvények visszatérési értéke nem csak a megszokott egyszerű típus lehet, hanem összetett típusú értéket is visszaadhat (pl. rekord vagy tömb típus). Nem lehet azonban file vagy object típusú értéket visszaadni (class típusút viszont igen).
Az eljáráshívás a következőképpen történik:
Természetesen az átadott paraméterek száma és típusa meg kell, hogy egyezzen az eljárásfejben deklarálttal. {$X+} állapotban függvény hívása is lehet önálló utasítás (ez eléggé elterjedt pl. Windows API függvények hívásakor, ezért az X direktíva alapértelmezésben be van kapcsolva). A függvények szerepelhetnek kifejezésben is, az eljárások nem.
Object Pascal nyelvben minden azonosítót felhasználása előtt deklarálni vagy definiálni kell. Ha két, egymást kölcsönösen hívó függvényt vagy eljárást szeretnénk készíteni, akkor az első alprogram egy később deklarált alprogramot hívna. Ezt a problémát megoldhatjuk úgy, hogy a második alprogramot előzetesen deklaráljuk.
Az előzetes deklaráció az alprogramnak csak a fejlécét tartalmazza, amelyet a forward direktíva és egy pontosvessző követ. Ezután az előzetesen deklarált eljárást vagy függvényt ugyanúgy hívhatjuk, mint a már definiáltakat. Az előzetesen deklarált alprogram teljes definíciója ezután bárhol megadható. Ekkor már elég a procedure vagy function kulcsszó után csak a függvény vagy eljárás nevét megadni. Pl:
De lehet a következőképpen is:
Nincs szükség a forward kulcsszóra, ha a deklaráció egy egység interface részében, rekord vagy osztály definíciójában van, mert ezek mindig előzetes deklarációk.
A paraméterlistában a formális paramétereket a szokásos név: típus formában adhatjuk meg, pontosvesszővel elválasztva. Lehetőség van egy típus előtt több nevet felsorolni, valamint a névcsoportot megelőzheti a paraméter módját jelző direktíva. Az utolsó néhány paraméternek alapértelmezett értéket is adhatunk.
Paraméterek átadására és módjára vonatkoznak az alábbi direktívák:
Paraméterek cím szerinti átadásának csak régi stílusú stringek, nagyobb rekordok vagy tömbök esetén van értelme. A Delphi objektumok mindig változatlanul adódnak át, mivel maguk is hivatkozások. Ebből kifolyólag egy objektum referenciakénti átadásának nem igazán van értelme, hiszen ez "hivatkozás a hivatkozásra" átadásnak felel meg. (Persze néha elképzelhető, hogy épp erre van szükségünk.)
A paraméterátadás szempontjából érdekes még a Delphi hosszú stringjeinek esete, ezek ugyanis (bár maguk is hivatkozások) eltérően viselkednek az előbb leírtaktól. Az érték szerint átadott string csak memória felhasználás szempontjából viselkedik hivatkozásként (csak egy mutató kerül a verembe). Ha megváltoztatjuk a string értékét, az eredeti string változatlan marad. Referenciaként átadva az eredeti értéket változtathatjuk.
Az alprogramoknak érték és cím szerint átadott változók típusa tetszőleges lehet, kivéve a fájltípusokat (text, file of <típus>, file), amelyeket csak cím szerint adhatunk át.
Érdemes kiemelni két további lehetőséget, melyek segítségével eljárás- és függvénysablonokat tudunk definiálni.
Az alprogramok paraméterlistáján függvény és eljárás is szerepelhet. Ilyenkor létre kell hozni a megfelelő eljárás- és függvénytípusokat, pl.:
Az Object Pascal lehetővé teszi, hogy a formális paraméterlistán elhagyjuk a paraméterek típusát. Ezt a módszert akkor használhatjuk, ha az alprogram működése a típustól független.
Ha a típus nincs megadva, akkor egy típus nélküli pointer vesz át, de ez nem ugyanaz, mintha Pointer típusú paramétert várnánk! Erre bármilyen változó illeszkedik, míg a Pointer-re csak Pointer típusú. Ilyenkor kötelező minősíteni (var, out, const), mert érték szerint nem lehet átvenni címet. Az ilyen paramétereket csak úgy tudjuk felhasználni, ha explicit típuskényszerítést használunk.
Delphiben van mód néhány különleges paraméter használatára is, melyekkel az alprogramhívások nyújtotta lehetőségek bővülnek ki.
A formális paraméterlistában nyitott tömb használatával elérhetjük, hogy az alprogram tetszőleges méretű aktuális paramétertömbbel hívható legyen. A nyitott tömböt a következő módon deklaráljuk: array of Típus, ahol a Típus a tömb elemeinek típusát jelenti. (Ez nem összetévesztendő a dinamikus tömb típussal, amely változó deklarációjában fordul elő.) A tömb indexhatárait a Low() és High() függvény használatával kérdezhetjük le.
Nyitott tömb paraméter használata esetén az alábbiakra kell ügyelni:
A nyitott tömb paraméter használatát mutatja a következő példa :
Lehetőség van arra is, hogy olyan nyitott tömböt deklaráljunk, amelynek az elemei különböző típusúak lehetnek. Ezt az array of const vagy az ezzel ekvivalens array of TVarRec deklaráció megadásával használhatjuk. Az alprogramban az adott tömbelem típusát egy egyszerű case szerkezettel kérdezhetjük le. Tipikus példa ennek az eszköznek a használatára a SysUtils egységben található Format függvény, amely a C-beli printf-hez hasonlóan egy formátumstringbe helyettesíti be a paramétereit.
Példa a különféle típusok kezelésére (a SysUtils stringgé alakító függvényeit használja):
A Delphiben használhatunk tetszőleges hosszúságú string paramétert is a formális paraméterlistában, ehhez a paraméter típusát string helyett OpenString típusúnak kell deklarálni:
A formális paraméter hossza itt is mindig megegyezik az aktuális paraméter hosszával, ami bármilyen string típusú változó lehet. Arra azonban ügyeljünk, hogy ez a paraméterátadás nem használható érték szerinti és konstans paraméterátadásnál, ilyenkor a Delphi az ilyen típussal deklarált paramétereket hagyományos string típusúnak tekinti.
(Csak Delphi 4-től)
Azonos névvel, de különböző paraméterlistával és működéssel több alprogramot is deklarálhatunk az overload direktíva segítségével. A fordítóprogram az aktuális paraméterlista alapján dönti el, hogy melyiket hívja. Pl:
Ha a paraméterlista nem egyértelmű, fordítási hibát ad. Pl:
Túlterhelt alprogramok ugyanolyan „típusúak” kell, hogy legyenek, azaz függvényt és eljárást nem lehet keverni.
Osztályon belüli túlterhelés: Származtatott osztályban egy metódus overload direktívával való deklarálása egy új metódust fog létrehozni az öröklött metódus elrejtése nélkül, ha a paraméterlistája különbözik az ősétől. Virtuális metódus túlterhelése a reintroduce direktívával lehetséges:
Egy osztályon belül nincs lehetőség azonos néven több túlterhelt metódus exportálására a published szekcióban (máshol megengedett).
A Delphiben a szokásos módon írható le a rekurzió:
Bizonyos direktívákkal befolyásolhatjuk egy alprogramból generált kódot.
Az inline kulcsszóval azt jelezzük, hogy a fordító az alprogram hívásának helyére bemásolhatja az alprogram kódját. Ez nem jelent kötelezettséget, a fordító dönthet úgy, hogy egy adott helyen mégis alprogramhívást generál.
Ha egy alprogram különálló tárgykód fájlban (.OBJ) vagy DLL-ben található, akkor external deklarációt kell felvennünk hozzá. .OBJ fájl esetén csak az external; kulcsszót adjuk meg. DLL-nél meg kell adni a DLL fájl nevét és esetleg az exportált nevét string konstans formájában:
Általában a register-t célszerű használni, mivel ez a leggyorsabb, így ez az alapértelmezett is. A cdecl-re C/C++ függvény hívásakor lehet szükség, a stdcall-t pedig általános használatra javasolják külső kód meghívására. A pascal konvenció csak kompatibilitási okok miatt maradt meg.