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 :

 
Sélectionnez
procedure MyProc(x:Integer); inline;
 
begin
 
    // ...
 
end;
 
Sélectionnez
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é (+ 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 figures.

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 :

 
Sélectionnez
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és

Les exemples suivants vous indiquent comment déclarer et accéder à des champs et à des méthodes de classes imbriquées.

 
Sélectionnez
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 :

 
Sélectionnez
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 :

 
Sélectionnez
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és.

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 :

 
Sélectionnez
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 :

 
Sélectionnez
for compteur := Valeurinitiale to ValeurFinale do instructions

Soit :

 
Sélectionnez
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 compatible 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 :

 
Sélectionnez
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 multi-dimensionnel
 
  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) :

 
Sélectionnez
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) :

 
Sélectionnez
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.

 
Sélectionnez
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 :

 
Sélectionnez
 {$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:

 
Sélectionnez
{$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 :

 
Sélectionnez
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:

 
Sélectionnez
{$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 :

 
Sélectionnez
type
 
    Temperature = type double;
 
    {$NODEFINE Temperature}
 
    {$HPPEMIT 'typedef double Temperature'}

IV-E. NOINCLUDE

Syntaxe:

 
Sélectionnez
{$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.