I. Convention d'appel INLINE▲
Dans la déclaration d'une procédure ou d'une fonction, vous pouvez spécifier la nouvelle directive INLINE qui permet d'améliorer l'exécution des fonctions et des procédures. Si la fonction ou la procédure répond à certains critères, le compilateur insérera le code directement, plutôt que de produire un appel (Call xxxx).
Elle permet d'obtenir un code plus rapide par une optimisation de l'exécution, mais aux dépens de l'espace mémoire. Cette directive dupliquant le code à chaque fois. Son comportement est proche des macro-instructions disponibles dans les langages assembleurs (elle existait déjà sous TP6 & 7). La directive INLINE générera toujours un fichier binaire de taille plus importante. Elle est employée comme d'autres directives telles que cdecl ou safecall.
Par exemple :
procedure
MyProc(x:Integer
); inline
;
begin
// ...
end
;
function
MyFunc(y:Char
) : String
; inline
;
begin
// ..
end
;
La directive inline est une suggestion faite au compilateur. Il n'y a aucune garantie que le compilateur duplique le code d'une routine particulière, car il existe certaines circonstances où cette directive ne peut pas être utilisée (+ de 10 cas recensés), par exemple :
- les routines contenant de l'assembleur ;
- les constructeurs et les destructeurs ;
- le bloc de programme principal, d'initialisation et de finalisation d'unité ;
- les routines qui utilisent des tableaux ouverts comme paramètres ;
- etc.
Si vous modifiez l'implémentation d'une routine INLINE, vous provoquerez une recompilation de toutes les unités qui l'utilisent. C'est une modification des règles traditionnelles de reconstruction qui sont déclenchées seulement par des changements de la section d'interface d'une unité. La directive de compilation {$INLINE [ON(default), OFF, AUTO]} vous offrira un contrôle plus fin des différents cas de figure.
II. Déclarations imbriquées▲
II-A. Déclaration de types imbriqués▲
Des déclarations de types peuvent être imbriquées dans une déclaration de classe. Les types imbriqués sont employés dans tout le framework .NET, et généralement dans toute Programmation Orientée Objet. Ce type de déclaration présente l'intérêt de regrouper les types « conceptuellement associés », et d'éviter des collisions de noms. La même syntaxe peut être désormais employée avec le compilateur Win32 de Delphi 2005.
Exemple de déclaration de types imbriqués :
type
className = class
[abstract
| sealed
] (ancestorType)
memberList
type
DeclarationDeTypeImbriqué
memberList
end
;
Là où DeclarationDeTypeImbriqué suit la syntaxe de déclaration de type définie d'ordinaire dans la partie de déclaration nommée Type.
Les déclarations de types imbriqués prennent fin dès la première occurrence d'un élément du langage autre qu'un identifieur, par exemple, procedure , class, type, ou tous les spécificateurs de portée.
Les règles normales d'accès s'appliquent aux types imbriqués et à leurs types contenants, c'est-à-dire de niveau supérieur que j'appellerai classe conteneur.
Un type imbriqué peut accéder à une variable d'instance (champ, propriété, ou méthode) de sa classe conteneur, mais il doit utiliser une référence d'objet pour le faire. Un type imbriqué peut accéder à des champs de classe, des propriétés de classe et des méthodes statiques de la classe sans référence d'objet, mais dans ce cas les règles normales de visibilité de Delphi s'appliquent.
Les types imbriqués n'augmentent pas la taille de la classe conteneur. En d'autres termes, créer une instance de la classe conteneur ne crée pas par la même occasion une instance du type imbriqué. Les types imbriqués sont associés à leurs classes conteneurs uniquement par le contexte de leur déclaration.
II-A-1. Déclarer et accéder aux classes imbriquées▲
Les exemples suivants vous indiquent comment déclarer et accéder à des champs et à des méthodes de classes imbriquées.
type
TClassConteneur = class
strict
private
myField: Integer
;
public
type
// Début de déclaration de la classe imbriquée
TClassImbriquee = class
public
monChampImbrique: Integer
;
procedure
ProcImbrique;
end
; // Fin de déclaration de la classe imbriquée
procedure
ProcConteneur; // Ici prend fin de déclaration du type imbriqué
end
;
Pour appliquer la méthode ProcImbrique d'une classe imbriquée, vous devez qualifier son nom avec le nom de la classe conteneur.
Par exemple :
procedure
TClassConteneur.TClassImbriquee.innerProc;
begin
...
end
;
Pour accéder aux membres du type imbriqué, employez la notation habituelle (notation pointée : Classe.Membre). Par exemple :
var
x: TClassConteneur;
y: TClassConteneur.TClassImbriquee;
begin
x := TClassConteneur.Create;
x.ProcConteneur;
...
y := TClassConteneur.TClassImbriquee.Create;
y.ProcImbrique;
II-B. Déclarations de constantes imbriquées▲
Des constantes peuvent être déclarées dans des types de classe de la même manière que la section type imbriqué.
Les sections constantes sont terminées de la même manière que les sections de type imbriquées.
Les constantes typées ne sont pas supportées, vous ne pouvez donc pas déclarer des constantes imbriquées telles que Currency, ou TdateTime.
Les constantes imbriquées peuvent être de n'importe quel type simple : ordinaux et sous-ensemble ordinaux, énumération, String et Real.
Exemple de code de déclaration de constantes imbriquées :
type
TMyClass = class
const
x = 12
;
y = TMyClass.x + 23
;
procedure
Hello;
private
const
s = 'A string constant'
;
end
;
begin
writeln(TMyClass.y); // Writes the value of y, 35.
end
.
III. Itération sur des conteneurs et des classes▲
III-A. Rappel de l'instruction For▲
Une instruction For, à la différence d'une instruction Repeat ou While, exige que vous indiquiez explicitement le nombre d'itérations souhaitées. La syntaxe d'une instruction FOR est soit :
for
compteur := Valeurinitiale to
ValeurFinale do
instructions
Soit :
for
compteur := Valeurinitiale downto
ValeurFinale do
instructions
Où :
- Compteur est une variable locale (déclarée dans le bloc contenant l'instruction for) de type ordinal, sans aucun qualificateur ;
- ValeurInitiale et ValeurFinale sont des expressions compatibles avec une assignation de compteur (type scalaire) ;
- Instructions est une instruction simple ou structurée qui ne modifie pas la valeur du compteur.
III-B. Itération sur des conteneurs▲
Delphi 2005 supporte, pour .NET et Win32, l'itération sur des éléments d'une collection (iteration over containers). Les itérations de conteneur suivantes sont reconnues par le compilateur :
- for Element in ArrayExpression do Instruction ;
- for Element in StringExpression do Instruction ;
- for Element in SetExpression do Instruction ;
- for Element in CollectionExpression do Instruction.
Le type de la variable d'itération doit correspondre au type contenu dans le conteneur. Pour chaque itération de la boucle, la variable d'itération pointe sur le membre courant de la collection. Comme avec les boucles classiques, la variable d'itération doit être déclarée dans le même bloc que pour l'instruction.
La variable d'itération ne peut pas être modifiée dans la boucle. Ceci inclut l'assignation et passage de paramètres à un paramètre de var d'une procédure. Procéder ainsi aura comme conséquence une erreur lors de la compilation.
Les expressions de tableau peuvent être du type simple ou multidimensionnel, de longueur fixe, ou dynamique. Le tableau est parcouru dans l'ordre croissant, commençant à partir de l'indice de limite inférieure et finissant à l'indice max du tableau.
L'exemple de code ci-dessous parcourt les différents types de tableaux :
type
TIntArray = array
[0
..9
] of
Integer
;
TGenericIntArray = array
of
Integer
;
var
IArray1: array
[0
..9
] of
Integer
= (1
, 2
, 3
, 4
, 5
, 6
, 7
, 8
, 9
, 10
);
IArray2: array
[1
..10
] of
Integer
= (1
, 2
, 3
, 4
, 5
, 6
, 7
, 8
, 9
, 10
);
IArray3: array
[1
..2
] of
TIntArray = ((11
, 12
, 13
, 14
, 15
, 16
, 17
, 18
, 19
, 20
),
(21
, 22
, 23
, 24
, 25
, 26
, 27
, 28
, 29
, 30
));
MultiDimTemp: TIntArray;
IDynArray: TGenericIntArray;
I: Integer
;
begin
for
I in
IArray1 do
begin
// Fait qq chose avec I...
end
;
// L'indexation commence à 1, la limite de rangée inférieure.
for
I in
IArray2 do
begin
// Fait qq chose avec I...
end
;
// Itération sur un tableau multidimensionnel
for
MultiDimTemp in
IArray3 do
// indexation 1..2
for
I in
MultiDimTemp do
// indexation 0..9
begin
// Fait qq chose avec I...
end
;
// Itération sur un tableau dynamique
IDynArray := IArray1;
for
I in
IDynArray do
begin
// Fait qq chose avec I...
end
;
Le code suivant concerne l'itération sur des expressions de type chaîne (String) :
var
C: Char
;
S1, S2: String
;
Counter: Integer
;
OS1, OS2: ShortString;
AC: AnsiChar;
begin
S1 := 'Now is the time for all good men to come to the aid of their country.'
;
S2 := ''
;
for
C in
S1 do
S2 := S2 + C;
if
S1 = S2 then
WriteLn('SUCCESS #1'
);
else
WriteLn('FAIL #1'
);
OS1 := 'When in the course of human events it becomes necessary to dissolve...'
;
OS2 := ''
;
for
AC in
OS1 do
OS2 := OS2 + AC;
if
OS1 = OS2 then
WriteLn('SUCCESS #2'
);
else
WriteLn('FAIL #2'
);
end
.
Le code suivant concerne l'itération sur des expressions de type ensemble (Set) :
type
TMyThing = (one, two, three);
TMySet = set
of
TMyThing;
TCharSet = set
of
Char
;
var
MySet: TMySet;
MyThing: TMyThing;
CharSet: TCharSet;
{$IF DEFINED(CLR)}
// Indique si on compile pour DOT NET
C: AnsiChar;
{$ELSE}
C: Char
;
{$IFEND}
begin
MySet := [one, two, three];
for
MyThing in
MySet do
begin
// Fait qq chose avec MyThing...
end
;
CharSet := [#0
..#255
];
for
C in
CharSet do
begin
// Fait qq chose avec C...
end
;
end
.
III-C. Itération sur des classes▲
Pour utiliser la construction de boucle for-in sur une classe, la classe doit implémenter un pattern de collection imposé (prescribed collection pattern). Un type qui implémente le pattern de collection doit avoir les attributs suivants :
- la classe doit posséder une méthode publique d'instance appelée GetEnumerator(). La méthode GetEnumerator() doit renvoyer une classe, une interface, ou un type record ;
- la classe, l'interface, ou le record renvoyé par GetEnumerator() doit contenir une méthode d'instance publique appelée MoveNext(). La méthode MoveNext() doit renvoyer un booléen ;
- la classe, l'interface, ou le record renvoyé par GetEnumerator() doit contenir une propriété publique en lecture seule appelée Current. Le type de la propriété Current doit être du même type que celui contenu dans la collection.
Si le type de l'énumérateur retourné par la méthode GetEnumerator() implémente l'interface IDisposable, le compilateur appellera la méthode Dispose du type lorsque la boucle se terminera.
Le code suivant concerne l'itération sur un conteneur « énumérable » sous Delphi 2005.
type
TMyIntArray = array
of
Integer
;
TMyEnumerator = class
Values: TMyIntArray;
Indice: Integer
;
public
constructor
Create;
function
GetCurrent: Integer
;
function
MoveNext: Boolean
;
property
Current: Integer
read
GetCurrent;
end
;
TMyContainer = class
public
function
GetEnumerator: TMyEnumerator;
end
;
constructor
TMyEnumerator.Create;
begin
inherited
Create;
Values := TMyIntArray.Create(100
, 200
, 300
);
Indice := -1
;
end
;
function
TMyEnumerator.MoveNext: Boolean
;
begin
if
Indice < High(Values) then
begin
Inc(Indice);
Result := True
;
end
else
Result := False
;
end
;
function
TMyEnumerator.GetCurrent: Integer
;
begin
Result := Values[Indice];
end
;
function
TMyContainer.GetEnumerator: TMyThing;
begin
Result := TMyEnumerator.Create;
end
;
var
MyContainer: TMyContainer;
I: Integer
;
Counter: Integer
;
begin
MyContainer := TMyContainer.Create;
Counter := 0
;
for
I in
MyContainer do
Inc(Counter, I);
WriteLn('Counter = '
, Counter);
end
.
Les classes suivantes et leurs descendantes supportent la syntaxe For-in :
- TList ;
- TCollection ;
- TStrings ;
- TInterfaceList ;
- TComponent ;
- TMenuItem ;
- TCustomActionList ;
- TFields ;
- TListItems ;
- TTreeNodes ;
- TtoolBar.
IV. Les directives de compilations▲
IV-A. MESSAGE▲
Syntaxe: {$MESSAGE HINT|WARN|ERROR|FATAL 'texte' }
La directive MESSAGE permet au code source d'émettre des conseils, des avertissements, et des erreurs comme le fait le compilateur. C'est une directive semblable à #emit ou pragma warn du C et C++. Le type de message (HINT, WARN, ERROR, ou FATAL) est optionnel. Si aucun type de message n'est indiqué, celui par défaut est HINT. Le texte doit être délimité par de simples quotes.
Exemples :
{$MESSAGE 'Boo!'}
émet un conseil (hint)
{$Message Hint 'Feed the cats'}
émet un conseil (hint)
{$messaGe Warn 'Looks like rain.'}
émet un avertissement ( warning )
{$Message Error 'Not implemented'}
émet une erreur,la compilation continue
{$Message Fatal 'Bang. Yer dead.'}
émits une erreur,la compilation prend fin
IV-B. REALCOMPATIBILITY▲
Notez que l'utilisation du type Real48 n'est plus recommandé (deprecated) sous .NET. Préférez dans la plupart des cas le type double.
IV-C. UNSAFE▲
Syntaxe :
{$UNSAFECODE ON}
ou {$UNSAFECODE OFF}
Spécifique à la plateforme Dot Net.
La directive UNSAFECODE indique si le mot-clé unsafe est accepté par le compilateur. Avec {$UNSAFECODE ON}, vous pouvez identifier des procédures et des fonctions avec le mot-clé unsafe, par exemple :
procedure
unsafeProc; unsafe
;
begin
end
;
Cette directive est utile seulement en .NET (en Win32 est-elle ignorée). Unsafe signifie que le code sera compilé en non managé.
IV-D. NODEFINE▲
Syntaxe :
{$NODEFINE Identifieur}
La directive NODEFINE empêche le symbole indiqué d'être inclus dans le fichier d'entête généré pour le C++, tout en permettant la génération d'information dans le fichier OBJ.
Quand vous employez NODEFINE, il est de votre responsabilité de définir n'importe quels types nécessaires avec HPPEMIT.
Par exemple :
type
Temperature = type
double
;
{$NODEFINE Temperature}
{$HPPEMIT 'typedef double Temperature'}
IV-E. NOINCLUDE▲
Syntaxe :
{$NOINCLUDE NomDeFichier}
La directive NOINCLUDE empêche le fichier indiqué d'être inclus dans les fichiers d'entête produits pour le C++.
Par exemple, {#$$NOINCLUDE>Unit1} enlève #include Unit1.