Les possibles évolutions du langage Delphi 2006

Laurent Dardenne

Vous trouverez dans ce tutoriel un aperçu des possibles évolutions du langage Delphi Win32 sous Borland Developper Studio 2006. Il s'agit bien sûr de possibles évolutions étant donné qu'à ce jour, fin octobre 2005, le produit n'est pas encore finalisé.

Sous réserve de ma traduction de l'aide en ligne de Delphi 2006 bêta. ♪

- Titre : Les possibles évolutions du langage Delphi 2006

- Auteur : Laurent Dardenne

- Parution : 4 novembre 2005

- Mise à jour : 4 novembre 2005

- Licence : Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright ® 2005 Laurent Dardenne. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Sommaire

- I. Rappel

- II. Alias de type

- III. Nouveaux spécificateurs de visibilité pour les membres de classe

- IV. Variable de classe

- V. Propriété de classe

- VI. Surcharge de propriété indexée

- VII. Méthode Final

- VIII. Méthode statique de classe

- IX. Classe scellée (sealed)

- X. Classe abstraite

- XI. Syntaxe d'assistance de classe (Class Helper)

- XI-A. Un possible exemple d'utilisation

- XII. Les records

- XII-A. Les records ont désormais de la classe

- XII-B. Surcharge d'opérateurs dans les enregistrements

- XII-C. Déclaration des surcharges d'opérateurs

- XII-D. Les records dynamiques

- XIII. Directives de compilation

- XIII-A. Experimental

- XIII-B. Description

- XIII-C. Extension de l'exécutable

- XIII-D. Directives de compilation pour les bibliothèques ou les objets partagés

- XIII-E. Vérification des exceptions sur les flottants (Floating Point Exception Checking)

- XIII-F. Informations de méthode

- XIII-G. Champs d'entête PE (portable executable)

- XIV. Conclusions

- XV. Liens

I - Rappel

Vous trouverez dans le tutoriel suivant le rappel des évolutions du langage sous Delphi 2005 - Win32.
Pour un aperçu sur certaines évolutions du langage identique sur les plateformes Win32 et .NET, consultez le tutoriel sur le langage Delphi .NET.

II - Alias de type

Delphi permettait déjà de créer des alias de type tels que

type TMaString = string;

autorisant la déclaration de variable suivante :

var
Chaine1: TMaString;
Chaine2: string;

Ici Chaine1 et Chaine2 sont du même type.
Si on souhaite distinguer le type exact de ces deux variables, cela n'est pas possible, car TMaString est un nom de type différent de string, mais si on veut créer un autre type, vu comme tel par le compilateur, il faut utiliser la syntaxe suivante :

type TMaString = type string;

Dans ce cas le passage en paramètre par référence d'une variable de ce type provoquera une erreur de compilation :

type
TMaString1 = string;
TMaString2 = type string;

var
Chaine: string;
Chaine1: TMaString1;
Chaine2: TMaString2;

procedure Affiche(S:TMaString2);
begin
end;

procedure Affiche_Var(Var S:TMaString2);
begin
end;

begin
Chaine:='Type Chaine classique';
Chaine1:=Chaine;
// l'affectation ne pose pas de problème
Chaine2:=Chaine;

// Le passage en paramètre à une procédure const ou par valeur ne pose pas de problème
Affiche(Chaine1);

// Le passage en paramètre à une procédure var, provoque l'erreur (E2033) :
// Les types des paramètres var originaux et formels doivent être identiques.
Affiche_Var(Chaine1);

Le code précédent provoque l'erreur suivante :

E2033 : Les types des paramètres var originaux et formels doivent être identiques.

III - Nouveaux spécificateurs de visibilité pour les membres de classe

Delphi ajoute deux spécificateurs de visibilité supplémentaires strict private (privée stricte) et strict protected (protégée stricte). Ils sont ordonnés ainsi, bien que dans une déclaration de classe cette notion d'ordre ne soit pas effective :

- strict private ;

- private ;

- strict protected ;

- protected ;

- public.

Texte issu de la documentation de DELPHI 2006 bêta
Les membres de classes dont la visibilité est strict private ne sont accessibles que dans la classe dans laquelle ils sont déclarés. Ils ne sont pas visibles pour les procédures ou fonctions déclarées dans la même unité.

Les membres de classes dont la visibilité est strict protected sont visibles dans la classe dans laquelle ils sont déclarés et dans toutes les classes dérivées, quel que soit l'endroit où elles sont déclarées.
Rappel :
Les membres au début de la déclaration d'une classe dont la visibilité n'est pas spécifiée sont par défaut public.

TMaClasse = class

strict private
// Visible seulement par les instances de la classe MaClasse
FStrictPrivate: Integer;

private
// Visible seulement par les instances de la classe MaClasse ou de l'unité
Fprivate: Integer;

strict protected
// Visible seulement par les instances de la classe MaClasse ou des classes dérivées
FStrictProtected: Integer;

protected
// Visible seulement par les instances de la classe MaClasse, des classes dérivées ou de l'unité
FProtected: Integer;

public
// Visible par tout code qui dispose d'un accès global à cette classe.
FVisible: Integer;

published
// Rend publique cette partie et génère également les informations de types à l'exécution.
// Entre autres rôles, les informations de types à l'exécution permettent à l'inspecteur
// d'objets d'accéder aux propriétés et aux événements.
property Visible: Integer read FVisible;
end;

Vous pouvez, dans une classe, déclarer un constructeur privé afin d'empêcher la création d'instances de cette classe.

IV - Variable de classe

Texte issu de la documentation de DELPHI 2006 bêta

Les champs de classe sont des champs de données d'une classe accessibles sans une référence d'objet.
La déclaration de bloc class var vous permet d'introduire un bloc de champs de classe dans une déclaration de classe.

Un bloc class var se termine par :

- une autre déclaration class var ;

- une déclaration de procédure ou fonction (c'est-à-dire une méthode) (y compris des procédures de classe et des fonctions de classe) ;

- une déclaration de propriété (y compris des propriétés de classe) ;

- une déclaration de constructeur ou de destructeur ;

- un spécificateur de portée de visibilité (public, private, protected, published, strict private et strict protected).

Par exemple :

TDBBool = record
Private
// Champ privé pour stocker la valeur du type
FValeur : integer;
Constructor Create(AValeur: IDBType);
procedure SetValeur(AValeur: IDBType);

Public
// Champs statiques de classe.
Class var dbFalse: TDBBool;
Class var dbNull : TDBBool;
Class var dbTrue : TDBBool;
...

Il est préférable de répéter la déclaration de class var, car dans certains cas vous rencontrerez quelques problèmes :

Public
// champs de classe, static
Class var
dbFalse: TDBBool;
dbNull : TDBBool;
dbTrue : TDBBool;
// private
FValeur : integer;

La mise en commentaire de Private provoque un effet de bord.

Le code suivant permet d'accéder aux champs de classe, déclarés ci-dessus, en préfixant le nom du champ avec le nom de la classe propriétaire :

Begin
TDBBool.dbNull.FValeur:=cdbNull;
TDBBool.dbFalse.FValeur:=cdbFalse;
TDBBool.dbTrue.FValeur:=cdbTrue;
end;

Les variables de classe sont accessibles dans les classes dérivées :

Type
TMaClass= Class
VariableInstance :String;
class var VariableDeClasse_TMaClass :String;
end;

TDescendant= Class(TMaClass)
class var VariableDeClasse_TDescendant :String;
end;

var Test          : TMaClass;
TestDescendant: TDescendant;

begin
Test.VariableDeClasse_TMaClass:='Test';
TDescendant.VariableDeClasse_TDescendant:='Descendant';
TestDescendant.VariableDeClasse_TDescendant:='Autre appel possible';

TDescendant.VariableDeClasse_TMaClass:='Modification';
end.

V - Propriété de classe

Les propriétés de classe sont des propriétés d'une classe accessibles sans une référence d'objet.
Une propriété de classe ne peut pas être publiée et ne peut pas avoir de définition de valeur stockée ou par défaut. De plus le champ associé à la propriété de classe doit être au minimum un champ de classe et les possibles accesseurs de propriétés de classes doivent être déclarés comme méthodes statiques de classe.

Le non-respect de ces deux règles provoquera l'erreur de compilation suivante :

E2355 : L'accesseur de la propriété de classe doit être un champ de classe ou une méthode statique de classe.

Voici un exemple d'utilisation :

type
TMaClass = class
strict private
class var FY : Integer;
class var FX : Integer;

strict protected
// Remarque : les accesseurs des propriétés de classe doivent être déclarés comme méthodes de classe statiques.
class function GetX: Integer; static;
class procedure SetX(val: Integer); static;

public
class property X: Integer read GetX write SetX;
class property Y: Integer read FY write FY;
end;

class function TMaClass.GetX: Integer;
begin
Result:=FX;
end;

class procedure TMaClass.SetX(val: Integer);
begin
FX:=val;
end;

L'accès aux propriétés de classe se fait de la manière suivante :

begin
TMaClass.X:=9;
TMaClass.y:=7;
Writeln(TMaClass.X,' ',TMaClass.Y);
Readln;
end.

VI - Surcharge de propriété indexée

Il est possible de surcharger la définition d'une propriété afin d'utiliser un ou plusieurs index de types différents. Si une classe a une propriété par défaut, vous pouvez accéder à cette propriété en utilisant la forme abrégée object[index] équivalente à object.property[index].

Par exemple, MonObjet.Strings[7] peut s'abréger en MonObjet[7]. Une classe ne peut avoir qu'une seule propriété par défaut.

type
TMaClass = class
FList : Array[1..10] of String;

function GetItem(Index: string): string; overload;
procedure SetItem(Index:string; Value: string); overload;

function GetItem(Index: Integer): string; overload;
procedure SetItem(Index: Integer; Value: string); overload;

property Item[Index: string]: string
read GetItem write SetItem; default;

property Item[Index: integer]: string
read GetItem write SetItem; default;
end;

Les deux propriétés sont déclarées avec le mot clé default sinon il n'est pas possible de compiler cet exemple. Il semblerait que la surcharge ne soit possible que pour la propriété par défaut d'une classe.

Voici un exemple d'appel :

var Propriete : TMaClass;

begin
Propriete:=TMaClass.Create;
Propriete[1]:='Premier';
Affiche(Propriete);

Writeln('Accès integer '+Propriete[1]);
Writeln('Accès String '+Propriete['Premier']);
readln;

Propriete['Premier']:='Deux';
...
end;

VII - Méthode Final

Cette nouvelle fonctionnalité permet à Delphi d'utiliser le concept de méthode virtuelle finale. Quand le mot clé final est appliqué à une méthode virtuelle, aucune classe dérivée ne peut redéfinir cette méthode.
L'usage du mot clé final est une décision de conception importante qui permet de définir l'utilisation de la classe.

program MethodeFinal;

{$APPTYPE CONSOLE}

uses
SysUtils;

type
TMaClass= class (TObject)
Champ1 : integer ;
procedure FaitqqChose; virtual;
end;

TDescendantClass= class (TMaClass)
Champ2 : integer ;
procedure FaitqqChose; override; Final;
end;

TClassRedeclareMethodeFinal= class (TDescendantClass)
Champ3 : integer ;
procedure FaitqqChose; override;   //Erreur
// E2352 : Impossible de redéfinir une méthode finale
end;

procedure TMaClass.FaitqqChose;
begin
Writeln('Champ1 ',Champ1);
end;

procedure TDescendantClass.FaitqqChose;
begin
Writeln('Champ2 ',Champ2);
end;

var
MonInstance: TClassRedeclareMethodeFinal;

begin
MonInstance:=TClassRedeclareMethodeFinal.Create;
end.

L'ajout du mot clé final à la déclaration de la méthode FaitqqChose; override de la classe TDescendantClass provoque l'erreur de compilation suivante pour la déclaration de la classe TClassRedeclareMethodeFinal :

E2352 : Impossible de redéfinir une méthode finale.

VIII - Méthode statique de classe

Dans la version bêta de Delphi 2006 le comportement de cette directive pose quelques problèmes.

Texte issu de la documentation de DELPHI 2006 bêta
Les méthodes statiques de classe sont des méthodes d'une classe accessibles sans une référence d'objet. Elles fonctionnent de la même manière que les méthodes traditionnelles de classe sous Delphi Win32.

Pour rendre une méthode de classe statique, ajoutez le mot static à sa déclaration, par exemple

program MethodeStatic;
{$APPTYPE CONSOLE}

Type
TMaClass= Class
VariableInstance :String;
class var VariableDeClasse :String;
Class procedure Affiche2; // Méthode de classe
Class procedure Affiche; static; // Méthode statique de classe
procedure Traitement;
end;

Class procedure TMaClass.Affiche;
begin
VariableDeClasse :='Affiche : une seule occurrence pour cette variable de classe';

// La ligne suivante est gérée par le compilateur et n'est pas possible.
//VariableInstance :='Plusieurs occurrences pour cette variable d''instance.';

Writeln(VariableDeClasse);
//Writeln(Self.ClassName); // E2003 : Identificateur non déclaré
end;

Class procedure TMaClass.Affiche2;
begin
//VariableDeClasse :='Affiche2 : une seule occurrence pour cette variable de classe';
Writeln(VariableDeClasse);

if assigned(Self)
then Writeln(Self.ClassName);
end;

procedure TMaClass.Traitement;
begin
Writeln('Dans la méthode traitement');
end;

var Test : TMaClass;

begin
// Appel de méthode de classe.
TMaClass.Affiche2;
Test.Affiche2;
Test.Affiche;
Test:=TMaClass.Create;  // Self accessible dans Affiche2
Test.Affiche2;
Readln;
end.

Contrairement aux méthodes de classe Delphi Win32, les méthodes statiques de classe n'ont pas de paramètre Self et ne peuvent donc accéder à aucun membre de classe, en dehors des champs de classe. En outre les méthodes statiques de classe ne peuvent pas être déclarées virtuelles (Virtual).

IX - Classe scellée (sealed)

Cette nouvelle fonctionnalité permet à Delphi de déclarer une classe qui ne peut pas être étendue par le biais de l'héritage.
Une fois une classe déclarée scellée (sealed) elle ne peut plus être dérivée comme le montre l'exemple suivant :

program ClasseSealed;

{$APPTYPE CONSOLE}

uses
SysUtils;

type
TMaClass= class (TObject)
Champ1 : integer ;
procedure FaitqqChose; virtual;
end;

TSealedClass= class sealed (TMaClass)
Champ2 : integer ;
procedure FaitqqChose; override;
end;

// Erreur à la compilation
TImpossibleClass= class (TSealedClass)
Champ3 : integer ;
procedure FaitqqChose; override;
end;

procedure TmaClass.FaitqqChose;
begin
Writeln('Champ1 ', Champ1);
end;

procedure TSealedClass.FaitqqChose;
begin
Writeln('Champ2 ', Champ2);
end;

var
MonInstance: TSealedClass;

begin
MonInstance:=TSealedClass.Create;
end.

L'ajout du mot clé sealed dans la déclaration de classe TSealedClass= class (TMaClass) provoque l'erreur de compilation suivante pour la déclaration de la classe TImpossibleClass :

E2353 : Impossible d'étendre la classe sealed 'TSealedClass'

X - Classe abstraite

Dans la version bêta de Delphi 2006 le mot clé abstract est reconnu par le compilateur, mais son ajout ne modifie pas le comportement de la classe.

Cette nouvelle fonctionnalité permet à Delphi de déclarer la totalité d'une classe comme classe abstraite (abstract) ce qui implique que cette classe ne peut pas être instanciée. Une classe entière peut être déclarée abstraite même si elle ne contient aucune méthode virtuelle abstraite.Une classe entière peut être abstrait avoué même si elle ne contient aucune méthode virtuelle abstraite.
La syntaxe de déclaration est la suivante :

program Abstract;

{$APPTYPE CONSOLE}

uses
SysUtils;

type
TAbstractClass= class abstract (TObject)
ChampExistant : integer ;

procedure FaitqqChose;
end;

procedure TAbstractClass.FaitqqChose;
begin
Writeln('ChampExistant ',ChampExistant);
end;

var
MonInstance:TAbstractClass ;

begin
MonInstance:=TAbstractClass.Create; // Impossible !
end.

La compilation de ce code, incluant une création d'une instance de classe abstraite, provoque l'erreur suivante :

E2402 : Construction de l'instance de la classe abstraite 'TAbstractClass'.

Il n'est pas possible de déclarer une classe abstract sealed :

type
TAbstractClass= class abstract sealed (TObject)
ChampExistant : integer ;

procedure FaitqqChose;
end;

Provoque l'erreur suivante :

E2383 : ABSTRACT et SEALED ne peuvent être utilisés ensemble.

XI - Syntaxe d'assistance de classe (Class Helper)

Texte issu de la documentation de DELPHI 2006 bêta
Une assistance de classe est un type qui, lorsqu'il est associé à une autre classe, introduit des méthodes et propriétés supplémentaires qui peuvent être utilisées dans le contexte de la classe associée (ou ses descendants). Les assistances de classes permettent d'étendre une classe sans avoir recours à l'héritage.

Les assistances de classes permettent d'étendre une classe, mais elles ne doivent pas être considérées comme un outil de conception à utiliser pour développer un nouveau code. Elles ne doivent être utilisées que pour la liaison entre le langage et la RTL de la plateforme.

Leur création est due principalement aux problématiques de portage de la VCL sous .NET. Pour plus d'informations consultez le tutoriel Le langage Delphi pour .NET.

XI-A - Un possible exemple d'utilisation

Lors de la création d'une instance de classe dans un bloc With do begin end, on aimerait récupérer une référence sur cette instance transitoire pour la communiquer à un traitement. Si on peut accéder à ces membres, on ne peut malheureusement pas accéder à l'instance elle-même.

Sous Delphi 2005 et 2006 l'utilisation de Class Helper peut être une solution, bien que l'utilisation d'une variable locale soit la solution la plus triviale.
Tout compte fait une classe Delphi peut manipuler l'instance via le mot clé self, mais uniquement à l'intérieur d'une méthode. L'idée est donc d'ajouter une fonction membre qui renvoie self comme résultat.

Type

GetterClassHelper=Class Helper For TObject
Function  GetInstance:TObject;
end;

function GetterClassHelper.GetInstance:TObject;
begin
// Self fait référence à la variable en cours,
// et non à une possible instance de type GetterClassHelper.
Result:=Self;
end;

On choisi de modifier la classe TObject afin de bénéficier de cet ajout de méthodes sur toutes les classes ancêtres.

L'appel dans le bloc With-Do devenant :

begin
With TMonObjet.Create do
begin
...
Traitement(GetInstance); // Récupére la référence
...
end;
end.

Voici un programme de test :

program classhelper;

{$APPTYPE CONSOLE}

uses
SysUtils;

Type
TTest=Class
Nom: String;
end;

GetterClassHelper=Class Helper FOR TObject
Function WriteClassName:ShortString;
Function  GetInstance:TObject;
end;

Function GetterClassHelper.WriteClassName:ShortString;
begin
// Self fait référence au type Test, et non à GetterClassHelper.
Result:=Self.ClassName;
end;

function GetterClassHelper.GetInstance:TObject;
begin
// Self fait référence à la variable en cours,
// et non à une possible instance de type GetterClassHelper.
Result:=Self;
end;

Procedure Visualise(AInstance :TObject; NomVaraible:String);
begin
Writeln('Nom de la variable : ',NomVaraible);
Writeln;
Writeln('Instance de la classe '+ TTest(AInstance).WriteClassName+' dans la methode visualise : '+TTest(AInstance).Nom);
Writeln;
end;

Var Premier, Second : TTest;

begin
Premier:=TTest.Create;
Premier.Nom:='Je reference l''instance Premier';
visualise(Premier,'Premier');

// Teste si on récupère bien une référence sur l'instance Premier
// équivaut à :
//  Second:=Premier
Second:=TTest(Premier.GetInstance);
visualise(Second,'Second');
Readln;

Premier.Free;
Second:=Nil;

// Objectif final sur une classe personnelle
With TTest.Create do
begin
Nom:='Je reference l''instance transitoire dans le bloc With-do';
visualise(GetInstance,' ici elle n''a pas de nom');
Free;
end;
Readln;

// Objectif final sur une classe de la VCL
With TStringList.Create do
begin
visualise(GetInstance,' ici elle n''a pas de nom');
Free;
end;
Readln;
end.

Les dernières lignes nous montrent bien qu'un assistant de classe peut nous aider à compléter une hiérarchie de classe sans modifier le code source de la classe d'origine.
Cela peut être une solution pour compléter des classes pour lesquelles on ne dispose que des déclarations de la partie interface.

Dans notre exemple le code suivant ne pourra pas compiler, car Self n'a pas d'existence dans ce programme console :

With TTest.Create do
begin
Nom:='Je reference l''instance transitoire dans le bloc With-do';
visualise(self,' Dans notre cas self n''existe pas');
Free;
end;

Si dans une méthode d'une classe on utilise le mot clé self dans le bloc With do begin end, self fera référence à l'instance courante de la classe comportant la méthode en cours d'exécution, et non à l'instance de l'objet créé à la volée dans le bloc with.

Merci à Sylvain james pour cette dernière remarque.

Les membres d'un assistant de classe peuvent être :

- des méthodes de classes ;

- des variables de classes ;

- des propriétés.

Mais un assistant de classe ne peut pas déclarer de champ supplémentaire ni de méthode virtual.

Il n'est pas possible de déclarer un assistant de classe d'un assistant de classe.

On peut déclarer plusieurs assistants de classe pour une même classe, mais il semble que seul le dernier déclaré soit pris en compte et annule les précédents.

TDescellee= class Helper For TSealedClass
class var unChampDeClass : integer ;
procedure FaitqqChose;
Class procedure Affiche;
Constructor Create;
end;

Un assistant de classe peut être utilisé, comme le laisse supposer le dernier exemple, étendre une classe scellée (Sealed).

Notez que les assistants de classe ressemblent aux Extension Methods du C# 3.0.

XII - Les records

L'utilisation des directives StrictPrivate, Private et Public est désormais possible dans un enregistrement.

Afin d'aborder rapidement le sujet, déclarons dans une unité un type Record utilisant ces directives :

unit TestRecord;

interface

Type
TUAdvancedRecord = record
Strict private
Valeur_StrictPrivate : integer;
Private
Valeur_Private : integer;
Public
Valeur_Public: Integer;
end;

implementation

end.

Il nous reste à mettre en évidence les règles de portée que ces directives permettent en utilisant le programme suivant :

program NouveauRecord1;

{$APPTYPE CONSOLE}

uses
SysUtils,
TestRecord in 'TestRecord.pas';

type
TAdvancedRecord = record
Strict private
Valeur_StrictPrivate : integer;
Private
Valeur_Private : integer;
Public
Valeur_Public: Integer;
end;

var TypeRecordDansLaMemeUnite : TAdvancedRecord;
TypeRecordDansUniteDifferente :TUAdvancedRecord;

begin

with TypeRecordDansLaMemeUnite do
begin
//Valeur_StrictPrivate:=1; // error E2003 : Identificateur non déclaré 'Valeur_StrictPrivate'
Valeur_Private:=2;
Valeur_Public:=3;
end;

with TypeRecordDansUniteDifferente do
begin
//Valeur_StrictPrivate:=1; // error E2003 : Identificateur non déclaré 'Valeur_StrictPrivate'
//Valeur_Private:=2; // error E2003 : Identificateur non déclaré 'Valeur_Private'
Valeur_Public:=3;
end;
end.

Les membres déclarés avec la directive :

- Strict private sont accessibles uniquement par les membres du Record ;

- Private sont accessibles uniquement dans l'unité où le Record est déclaré ;

- Public sont accessibles dans tous le code.

XII-A - Les records ont désormais de la classe

Le type record est désormais considéré soit comme traditionnel, soit comme avancé.
La dénomination traditionnel concerne le type record qu'on connait depuis le Pascal, la dénomination avancée est un record ayant, sur certains points, un comportement plus proche d'une classe.

Déjà sous Delphi 2005 .NET le type record avait été modifié pour correspondre au principe qui veut que tout soit objet sous .NET. Leur existence sous Delphi 2005 Win32 n'ayant pas été documentée, c'est désormais chose faite.

Texte de la documentation de Delphi 2006 bêta

En plus des types record traditionnels, Delphi autorise des types record plus complexe et plus proche d'une
classe.
En plus des champs, les records peuvent avoir des propriétés et des méthodes (y compris des constructeurs),
des propriétés de classe, des méthodes de classe, des champs de classe, et des types imbriqués.

Voici une définition d'un type record simple avec certaines fonctionnalités de classes.
(note : le texte d'origine utilise l'expression anglaise 'class-like')

type
TMyRecord = record
type
TInnerColorType = Integer;
var
Red: Integer;

class var Blue: Integer;

procedure printRed();
constructor Create(val: Integer);

property RedProperty: TInnerColorType read Red write Red;

class property BlueProp: TInnerColorType read Blue write Blue;
end;

constructor TMyRecord.Create(val: Integer);
begin
Red := val;
end;

procedure TMyRecord.printRed;
begin
writeln('Red: ', Red);
end;

Cependant les records peuvent maintenant partager une grande partie des fonctionnalités des classes,
il y a quelques différences importantes entre les classes et les records.

- Les records ne supportent pas l'héritage.

- Les records peuvent contenir des parties variables, les classes ne le peuvent pas.

- Les records sont des types valeur, ainsi ils sont copiés lors d'affectation, passés par valeur,
et alloués sur la pile à moins qu'ils soient déclarés globalement ou explicitement alloués en utilisant
les fonctions New et Dispose. Les classes sont des types référence, elles ne sont donc pas copiées lors
d'une affectation, elles sont passées par référence, et elles sont allouées sur le tas (Heap).

- Les records permettent la surcharge d'opérateurs sur les plateformes Win32 et .NET; Les classes permettent la
surcharge d'opérateurs seulement sous .NET.

- Les records sont construits automatiquement, en utilisant un constructeur par défaut qui est sans argument,
mais les classes doivent être explicitement construites.
Puisque les records ont un constructeur par défaut, n'importe quel constructeur de record défini par
l'utilisateur doit avoir obligatoirement un ou plusieurs paramètres.

- Les types record ne peuvent pas avoir de destructeurs.

- Les méthodes virtuelles (celles indiquées avec les mots clés virtual, dynamic, et message) ne peuvent pas être
employées dans les types record.

- À la différence des classes, les types record sur la plateforme Win32 ne peuvent pas implémenter d'interfaces;
Cependant les records sur la plateforme .NET le peuvent.

Désormais les record comme les classes peuvent utiliser les déclarations de type imbriqué.

Vous trouverez un exemple plus avant dans le chapitre sur la surcharge d'opérateur.

XII-B - Surcharge d'opérateurs dans les enregistrements

Texte issu de la documentation de DELPHI 2006 bêta
Delphi .NET autorise la surcharge de certaines fonctions (ou « opérateurs ») dans les déclarations de classe et d'enregistrement. Le nom de la fonction opérateur correspond à une représentation symbolique dans le code source. Par exemple, l'opérateur Add correspond au symbole +. Le compilateur génère un appel à la surcharge appropriée, en faisant correspondre le contexte (c'est-à-dire le type de retour et le type des paramètres utilisés dans l'appel) à la signature de la fonction opérateur.
Le tableau suivant énumère les opérateurs Delphi pouvant être surchargés :

Opérateur

Catégorie

Signature de déclaration

Mappage de symbole

Implicit

Conversion

Implicit(a : type): resultType;

implicit typecast

Explicit

Conversion

Explicit(a: type): resultType;

explicit typecast

Negative

Unaire

Negative(a: type): resultType;

-

Positive

Unaire

Positive(a: type): resultType;

+

Inc

Unaire

Inc(a: type): resultType;

Inc

Dec

Unaire

Dec(a: type): resultType

Dec

LogicalNot

Unaire

LogicalNot(a: type): resultType;

not

BitwiseNot

Unaire

BitwiseNot(a: type): resultType;

not

Trunc

Unaire

Trunc(a: type): resultType;

Trunc

Round

Unaire

Round(a: type): resultType;

Round

Equal

Comparaison

Equal(a: type; b: type): Boolean;

=

NotEqual

Comparaison

NotEqual(a: type; b: type): Boolean;

<>

GreaterThan

Comparaison

GreaterThan(a: type; b: type) Boolean;

>

GreaterThanOrEqual

Comparaison

GreaterThanOrEqual(a: type; b: type): resultType;

>=

LessThan

Comparaison

LessThan(a: type; b: type): resultType;

<

LessThanOrEqual

Comparaison

LessThanOrEqual(a: type; b: type): resultType;

<=

Add

Binaire

Add(a: type; b: type): resultType;

+

Subtrac

Binaire

Subtract(a: type; b: type): resultType;

-

Multiply

Binaire

Multiply(a: type; b: type): resultType;

*

Divide

Binaire

Divide(a: type; b: type): resultType;

/

IntDivide

Binaire

IntDivide(a: type; b: type): resultType;

div

Modulus

Binaire

Modulus(a: type; b: type): resultType;

mod

ShiftLeft

Binaire

ShiftLeft(a: type; b: type): resultType;

shl

ShiftRight

Binaire

ShiftRight(a: type; b: type): resultType;

shr

LogicalAnd

Binaire

LogicalAnd(a: type; b: type): resultType;

et

LogicalOr

Binaire

LogicalOr(a: type; b: type): resultType;

ou

LogicalXor

Binaire

LogicalXor(a: type; b: type): resultType;

xor

BitwiseAnd

Binaire

BitwiseAnd(a: type; b: type): resultType;

et

BitwiseOr

Binaire

BitwiseOr(a: type; b: type): resultType;

ou

BitwiseXor

Binaire

BitwiseXor(a: type; b: type): resultType;

xor

Aucun autre opérateur ne peut être défini sur un enregistrement.

Les méthodes d'opérateurs surchargés ne peuvent pas être référencées par nom dans le code source :

y := x + x;   // Appelle TMaClasse.Add(a, b: TMaClasse): TMaClasse
y := x add x ; // Interdit !
y := TMaClasse.add(x, x); // Interdit !

Pour accéder à une méthode d'opérateur spécifique d'une classe spécifique, vous devez utiliser des transtypages explicites sur tous les opérandes :

A,Z : integer
Begin
A:= Z+TMaClasse(x);

Il n'existe aucune hypothèse concernant les propriétés distributives ou commutatives de l'opération. Pour les opérateurs binaires, le premier paramètre est toujours l'opérande gauche et le second paramètre l'opérande droit. En l'absence de parenthèses explicites, l'associativité est supposée être de gauche à droite.

En règle générale, les opérateurs ne doivent pas modifier leurs opérandes. À la place, ils renvoient une nouvelle valeur, construite en effectuant l'opération sur les paramètres.

XII-C - Déclaration des surcharges d'opérateurs

Les surcharges d'opérateurs sont déclarées dans des enregistrements avec la syntaxe suivante :

type
typeName = [record]
class operator Signature_de_déclaration (a: type): Type_résultant;
liste d'opérateur
end;

L'implémentation des opérateurs surchargés doit aussi inclure la syntaxe class operator :

class operator Name._de_type. Signature _de_déclaration (a: type): Type_résultant;

Exemples d'opérateurs surchargés pour un enregistrement permettant de simuler un type :

unit UTDBBool;

interface

type

TDBBool = record
Strict private
// Champ privé pour stocker la valeur du type
FValeur : integer;

// champs de classe
Class var FdbFalse : TDBBool;
Class var FdbNull  : TDBBool;
Class var FdbTrue  : TDBBool;

// Type interne et Privé
type
DBType = -1..1;  // Etendue du type

Private
// Le constructeur ne doit pas être accessible
// Private pour TDBBool.dbTrue:=TDBBool.Create(0);
Constructor Create(AValeur: DBType);
// Constantes internes et Privées
const
// Valeurs du type autorisé
// On ne reprend pas à l'identique les valeurs du type Boolean de Delphi
// Le zéro permet les tests x>0 pour true et x<0 pour false, voir aussi l'opérateur LogicalNot
cdbNull   : integer =0;
cdbFalse  : Integer =-1;
cdbTrue   : Integer =1;

Class procedure Init;Static;
Public
// Examine la valeur d'un DBBool.
// Renvoie true si la valeur de Fvaleur correspond à la valeur testée
Function IsNull  : Boolean;
Function IsFalse : Boolean;
Function IsTrue  : Boolean;

// Surcharge d'opérateur
Class operator Implicit(Avalue: Boolean): TDBBool;
Class operator Implicit(x: TDBBool): String;
Class operator Explicit(x: TDBBool): Boolean;
Class operator Equal(x,y : TDBBool):TDBBool; //  =
Class operator NotEqual(x,y : TDBBool):TDBBool; // <>
Class operator LogicalNot(x : TDBBool):TDBBool; // not
Class operator LogicalAnd(x,y : TDBBool):TDBBool; // and
Class operator LogicalOr(x,y : TDBBool):TDBBool; // or

// Convertit la valeur spécifiée en sa représentation String équivalente.
Function ToString: String;
// Propriétées en ReadOnly
Class Property dbFalse : TDBBool Read FdbFalse;
Class Property dbNull  : TDBBool Read FdbNull;
Class Property dbTrue  : TDBBool Read FdbTrue;
end;

implementation

uses Classes;

Constructor TDBBool.Create(AValeur: DBType);
Begin
FValeur:=AValeur;
end;

Class Procedure TDBBool.Init;
Begin
TDBBool.FdbNull.FValeur:=cdbNull;
TDBBool.FdbFalse.FValeur:=cdbFalse;
TDBBool.FdbTrue.FValeur:=cdbTrue;
end;

Function TDBBool.IsNull  : Boolean;
begin
Result:=(Fvaleur=cdbNull);
end;

Function TDBBool.IsFalse : Boolean;
begin
Result:=(Fvaleur=cdbFalse);
end;

Function TDBBool.IsTrue  : Boolean;
begin
Result:=(Fvaleur=cdbTrue);
end;

//  implémentation des Opérateurs

// Conversion implicite de boolean vers TDBBool.
// renvoie DBBool.FdbTrue pour true
// et DBBool.FdbFalse pour false
Class operator TDBBool.Implicit(Avalue: Boolean): TDBBool;
begin
If Avalue=True
then Result:=FdbTrue
else Result:=FdbFalse;
end;

// Conversion explicite de TDBBool vers boolean.
// Déclenche une exception si x est égal à cdbNull,
// sinon renvoie True ou False.
Class operator TDBBool.Explicit(x: TDBBool): Boolean;
begin
If x.FValeur=cdbNull
then raise EInvalidOperation.Create('TDBOOL:opération invalide')
else Result:= x.FValeur>cdbNull // x.FValeur=cdbTrue;
end;

// Opérateur d'égalité. Renvoi FdbNull si l'un des opérandes est égal à cdbNull,
// sinon renvoie FdbTrue ou FdbFalse:
Class operator TDBBool.Equal(x : TDBBool; y : TDBBool):TDBBool;
// ATTENTION : pas d'évaluation booléenne complète, cf C#
begin
if ((x.FValeur = cdbNull) or (y.FValeur =cdbNull))
then result:=FdbNull
else if x.FValeur = y.FValeur
then Result:=FdbTrue
else Result:=FdbFalse;
end;

// Opérateur d'inégalité. Renvoie FdbNull si l'un des opérandes est égal à FdbNull,
// sinon renvoie FdbTrue ou FdbFalse:
Class operator TDBBool.NotEqual(x,y : TDBBool):TDBBool;
// ATTENTION : pas d'évaluation booléenne complète, cf C#
Begin
if ((x.FValeur = cdbNull) or (y.FValeur =cdbNull))
then result:=FdbNull
else if x.FValeur <> y.FValeur
then Result:=FdbTrue
else Result:=FdbFalse;
end;

// Opérateur de négation logique. Renvoie  :
//  FdbTrue si l'opérande est égale à FdbFalse
//  FdbNull si l'opérande est égale à FdbNull,
//  ou FdbFalse si l'opérande est égale à FdbTrue.
Class operator TDBBool.LogicalNot(x : TDBBool):TDBBool;
begin
Result:=TDBBool.Create(-x.FValeur);
end;

// Opérateur ET ( AND ) logique. Renvoie
//  FdbFalse si l'un des opérandes est égal à FdbFalse,
//  FdbNull si l'un des opérandes est égal à FdbNull,
//  sinon renvoie FdbTrue.
Class operator TDBBool.LogicalAnd(x,y : TDBBool):TDBBool;
var Resultat:Integer;
begin
If x.FValeur < y.FValeur
then Resultat:=x.FValeur
else Resultat:=y.FValeur;
Result:=TDBBool.Create(Resultat);
end;

// Opérateur ET ( AND ) logique. Renvoie
//  FdbTrue si l'un des opérandes est égal à FdbTrue,
//  FdbNull si l'un des opérandes est égal à FdbNull,
//  sinon renvoie FdbFalse.
Class operator TDBBool.LogicalOr(x,y : TDBBool):TDBBool;
var Resultat:Integer;
begin
If x.FValeur > y.FValeur
then Resultat:=x.FValeur
else Resultat:=y.FValeur;
Result:=TDBBool.Create(Resultat);
end;

// Conversion implicite de TDBBool vers String.
Class operator TDBBool.Implicit(x: TDBBool): String;
begin
If x.Fvaleur > 0
then Result:='dbTrue'
else If x.Fvaleur < 0
then Result:='dbFalse'
else Result:='dbNull';
end;

// Surcharge la méthode ToString pour convertir un TDBBool vers une String.
Function TDBBool.ToString: String;
begin
Case Fvaleur of
-1: Result:='TDBBool.False';
0: Result:='TDBBool.Null';
1: Result:='TDBBool.True';
else Raise EInvalidOperation.Create('TDBOOL:opération invalide');
end;
End;

// Initialise les valeurs possibles du type
Initialization
TDBBool.Init;
end.

Les constructeurs de classe n'existant pas sous Win32 on utilisera la partie Initialization d'une unité pour simuler son comportement.

Voici l'implémentation d'un opérateur surchargé, qui ne modifie par ses opérandes, mais renvoie une nouvelle valeur :

// Opérateur ET ( AND ) logique. Renvoie
//  dbFalse si l'une des opérandes est égale à dbFalse,
//  dbNull si l'un des opérandes est égal à dbNull,
//  sinon renvoie dbTrue.
Class operator TDBBool.LogicalAnd(x,y : TDBBool):TDBBool;
var Resultat:Integer;
begin
If x.FValeur < y.FValeur
then Resultat:=x.FValeur
else Resultat:=y.FValeur;
Result:=TDBBool.Create(Resultat);
end;

Le constructeur n'alloue pas de mémoire, mais accède aux champs de l'enregistrement courant. Le programme de test de cet enregistrement :

program operateur;
{$APPTYPE CONSOLE}

uses
SysUtils,
UTDBBool in 'UTDBBool.pas',
classes;

var
a, b, c : TDBBool;

begin
//Initialisation des variables hors constructeur
a:=TDBBool.dbTrue;
// Assignation de la valeur de la variable a dans la variable b
b:=a;
b:=TDBBool.dbNull;
c:=TDBBool.dbFalse;

WriteLn('a = ',a.ToString,' not a =', (not a).ToString); // Appel  : operator LogicalNot
WriteLn('c =', c.ToString); // Appel  : operator LogicalOr
WriteLn('b = ', b.ToString,' not b =', (not b).ToString);
WriteLn('a and b =',(a and b).ToString); // Appel  : operator LogicalAnd
WriteLn('a or b =', (a or b).ToString); // Appel  : operator LogicalOr

Readln;

// l'instruction case n'est pas possible sur un type record
if b=TDBBool.dbTrue then
Writeln('b est dbTrue')
else
if b=TDBBool.dbFalse then
Writeln('b est dbFalse')
else Writeln('b est dbNull');

// a=c renvoi un résultat erroné
// if boolean(a=c) renvoi un résultat erroné
if boolean(a=c)=true
then Writeln('A = C');

//Valeur true ou false gérée
try
if boolean(a)=Boolean(c)  // Appel operator Explicit
then Writeln('Boolean : a et c sont identiques')
else Writeln('Boolean : a et c ne sont pas identiques');
except
On E:EInvalidOperation do
WriteLn('a ou c est NULL')
end;

//Valeur Null non gérée, mais opération valide
try
if Boolean(b)=True
then WriteLn('b est true definitivement')
else WriteLn('b n''est definitivement pas true ');
except
On E:EInvalidOperation do
WriteLn('b est NULL');
end;

//Valeur Null non gérée, mais opération valide
try
if boolean(a)=Boolean(b)
then Writeln('a et b sont identiques')
else Writeln('a et b ne sont pas identiques');
except
On E:EInvalidOperation do
WriteLn('b est NULL')
end;

Readln;

(* Résultats attendus :
not DBBool.True = DBBool.False
not DBBool.Null = DBBool.Null
DBBool.True & DBBool.Null = DBBool.Null
DBBool.True or DBBool.Null = DBBool.True

a et c ne sont pas identique
b est NULL
b est NULL
*)
end.

Pour accéder à une méthode d'opérateur spécifique d'une classe spécifique, vous devez utiliser des transtypages explicites sur tous les opérandes : if Boolean(b)=True

XII-D - Les records dynamiques

L'utilisation des enregistrements dynamiques reste identique :

program NouveauRecord2;
{$APPTYPE CONSOLE}

uses
SysUtils;

type
PMyRec=^MyRec;
MyRec=Record
A,B :byte;
Constructor Create(lA,lB:byte);
Procedure Ecrit(Name:String);
end;

Constructor MyRec.Create(lA,lB:byte);
begin
A:=lA;
B:=lB;
end;

Procedure MyRec.Ecrit(Name:String);
begin
Writeln(Name,' A=',A,' B=',B);
end;

procedure visu;
var T: MyRec;
begin
// T n'est pas initialisé
T.Ecrit('T locale');
end;

var Un : MyRec;
R  : PMyRec;
begin
//Variable globale initialisée à Zéro
Un.Ecrit('UN');
visu;

New(R);                    //Alloue une zone mémoire
R^.Create(1,2);            //Le constructeur réaffecte la même zone mémoire
Un.Create(1,2);            //Le constructeur réaffecte la même zone mémoire
Un:=MyRec.Create(10,16);   //L'assignation réaffecte la même zone mémoire
Dispose(R);                //Désalloue la zone mémoire
R:=Nil;
R^.Ecrit('R après NIL.');  //Violation d'accès
end.

Attention les variables globales sont initialisées à zéro, dans tous les autres cas vous devez appeler le constructeur ou initialiser les champs avant de manipuler un enregistrement.

XIII - Directives de compilation

XIII-A - Experimental

Il s'agit d'une directive de conseil. Vous pouvez utiliser cette directive pour désigner des unités qui sont dans un état de développement instable. Le compilateur émet un avertissement quand il construit une application qui utilise l'unité.

unit UMonObjet platform experimental;

XIII-B - Description

De portée globale, sa syntaxe est :

{$DESCRIPTION 'text'}

La directive $D insère le texte que vous avez spécifié dans l'entrée 'description de module' de l'entête d'un exécutable, d'une DLL, ou d'un package. Traditionnellement le texte est un nom, un numéro de version, une note de copyright, mais vous pouvez spécifier n'importe quel texte de votre choix.

Par exemple:

{$D 'Mon Application version 12.5'}

La chaine de caractères ne doit pas dépasser 256 octets. La description n'est habituellement pas visible par l'utilisateur final. Pour marquer votre fichier exécutable avec du texte descriptif, un numéro de version et des informations de copyright à l'usage des utilisateurs finaux, utiliser les ressources d'information de version.

Note :
Le texte doit être inclus entre quotes.

XIII-C - Extension de l'exécutable

Déjà présent sous Delphi 2005, sa syntaxe est :

{$E extension} ou {$EXTENSION extension}

La directive $E définit l'extension du fichier exécutable généré par le compilateur. Elle est souvent utilisée avec le mécanisme DLL ressource seulement. Par exemple, placer {$E deu} dans un module bibliothèque génère une DLL ayant l'extension .deu : nomfichier.deu. Si vous créez un module bibliothèque qui ne référence que des chaines et des fiches allemandes, vous pouvez utiliser cette directive pour produire une DLL ayant l'extension .deu.
Le code de démarrage de la bibliothèque d'exécution cherche une DLL dont l'extension correspond à la locale du système — pour l'allemand, il cherche .deu — et charge les ressources de cette DLL.

XIII-D - Directives de compilation pour les bibliothèques ou les objets partagés

Déjà présent sous Delphi 2005 et de portée globale, sa syntaxe est :

$LIBPREFIX 'chaine'

$LIBSUFFIX 'chaine'

$LIBVERSION 'chaine'

//exemple

$LIBPREFIX 'lib' ou $LIBPREFIX 'bpl'

$LIBSUFFIX ' '

$LIBVERSION ' '

$LIBPREFIX remplace le préfixe 'lib' ou 'bpl' par défaut dans le nom du fichier de destination.
Par exemple, vous pouvez spécifier {$LIBPREFIX 'dcl'} pour un package de conception, ou utiliser la directive suivante pour éliminer le préfixe entièrement :

{$LIBPREFIX ' '}

{$LIBSUFFIX ' '} ajoute le suffixe spécifié au nom du fichier de destination, avant l'extension. Par exemple, utilisez :

{$LIBSUFFIX '-2.1.3'}

dans quelquechose.pas pour générer : quelquechose-2.1.3.dll

$LIBVERSION ajoute une seconde extension au nom du fichier de destination après l'extension. Par exemple, utilisez

{$LIBVERSION '-2.1.3'}

dans quelquechose.pas pour générer : libquelquechose.dll.2.1.3

XIII-E - Vérification des exceptions sur les flottants (Floating Point Exception Checking)

Switch de portée globale, sa syntaxe est

{$FINITEFLOAT ON}, {$FINITEFLOAT OFF}

La directive $FINITEFLOAT contrôle les débordements sur les flottants (overflow et underflow), et les opérations invalides telles que la division par zéro.
Avec {$FINITEFLOAT ON}, qui est l'état par défaut, les résultats des calculs sur les flottants sont vérifiés, et une exception est déclenchée lorsqu'il y a un dépassement de capacités (overflow et underflow), ou une opération invalide.

Avec {$FINITEFLOAT OFF}, de tels calculs sur des flottants renverront NAN, -INF ou +INF.

Un temps de traitement supplémentaire est nécessaire pour vérifier les résultats des calculs sur des nombres à virgule flottante et pour déclencher des exceptions.
Si votre code Delphi utilise des opérations de virgule flottante, mais n'exige pas l'application stricte des exceptions de dépassement de capacités (overflow et underflow), vous pouvez utiliser {$FINITEFLOAT OFF} pour obtenir une exécution légèrement plus rapide.

Note :
La majorité du code sous .NET fonctionne sans contrôles de virgule flottante. Cependant, Delphi a traditionnellement fourni la sémantique stricte des nombres à virgule flottante.
Si vous avez du code Delphi qui se base sur des exceptions de dépassement de capacités (overflow et underflow), vous devriez maintenir la directive par défaut ({$FINITEFLOAT ON}).

XIII-F - Informations de méthode

Switch de portée locale, sa syntaxe est

{$METHODINFO ON} or {$METHODINFO OFF}

Par défaut à OFF.

La directive $METHODINFO est effective seulement lorsque les informations RTTI (runtime type information) ont été autorisées avec la directive {$TYPEINFO ON}.
Dans l'état {$TYPEINFO ON}, La directive $METHODINFO contrôle la génération de descripteurs de méthode plus détaillés dans le RTTI pour des méthodes dans une interface.

Bien que {$TYPEINFO ON} générera des informations RTTI pour des méthodes publiées, le niveau de l'information est limité. La directive $METHODINFO produit des informations RTTI beaucoup plus détaillées (et d'un nombre plus important) pour des méthodes, qui décrit comment les paramètres de la méthode devraient être passés sur la pile et/ou dans des registres.
Il est rare qu'une application utilise directement la directive $METHODINFO.
Les informations de méthodes augmentent considérablement la taille du fichier exécutable, et son usage n'est pas recommandé pour une utilisation courante.

Note
Le support du code web service du compilateur Delphi Win32 utilise les informations de descripteurs de méthodes afin de passer des paramètres reçus dans une trame réseau (network packet) à la méthode cible. {$METHODINFO ON} est employé seulement pour des types d'interface web service.

Voir la directive {$TYPEINFO }.

XIII-G - Champs d'entête PE (portable executable)

Flag de portée local, sa syntaxe est :

{$SetPEFlags <integer expression>} {$SetPEOptFlags <integer expression>}

Microsoft s'appuie sur les drapeaux d'entête PE (portable executable) pour permettre à une application d'indiquer la compatibilité avec des services de l'OS ou de demander des services avancés de l'OS.
Ces directives fournissent des options puissantes pour optimiser vos applications sur les systèmes 'high-end' NT.

Il n'y a aucune vérification des erreurs ou des masques de bit indiquée par ces directives. Si vous placez une mauvaise combinaison, vous pouvez corrompre votre fichier exécutable.

Ces directives vous permettent respectivement de positionner un ensemble de flags dans le champ Characteristics et dans le champ facultatif DLLCharacteristics d'un entête de fichier PE. La plupart des flags Characteristics, ensemble utilisant $SetPEFlags, sont spécifiques aux fichiers et aux librairies. Les flags DLLCharacteristics, ensemble utilisant $SetPEOptFlags, décrivent quand appeler le point d'entrée d'une DLL. La partie '<integer expression> dans ces directives peut inclure des identifiants de constantes Delphi, telles que les constantes IMAGE_FILE_xxxx définies dans Windows.pas.
Les constantes multiples doivent être un ensemble construit avec l'opérateur OR.
Vous pouvez inclure plusieurs fois ces directives dans le code source. Les valeurs des flags indiquées par des directives multiples sont strictement cumulatives :
si la première occurrence de la directive affecte $03 et la deuxième occurrence affecte $10, la valeur écrite dans le fichier exécutable au moment de l'édition de lien sera $13 (plus quelque autres que l'éditeur de liens place normalement dans les flags PE).

Ces directives affectent seulement le fichier de sortie (output file) s'il est inclus dans le code source avant l'édition des liens. Ceci signifie que vous devriez placer ces directives dans un fichier .dpr ou de .dpk, mais pas dans un fichier d'unité. Comme la directive de description de l'exe, ce n'est pas une erreur de placer ces directives dans du code source d'une unité, mais ces directives dans le code source d'une unité n'affecteront pas le fichier de sortie (exe ou DLL) à moins que le source de l'unité soit recompilé lorsque le fichier de sortie est linké.

XIV - Conclusions

Ces évolutions, certaines déjà présentent dans Delphi 2005, faciliteront, entre autres, le développement de code partagé sous Win32 et .NET en évitant le recours massif aux directives de compilation conditionnelle.
Pour ma part j'aurais apprécié pour les Record dynamique une syntaxe facilitant l'allocation et l'appel du constructeur en une passe, un peu comme sous TP 7.0 :-), ainsi qu'une instruction similaire au FreeAndNil pour la désallocation.

Quoi qu'il en soit ces nouveautés du langage sont déjà fort appréciables et il me tarde de découvrir celle de Highlander qui s'annonce révolutionnaire pour Delphi, mais chaque chose en son temps ;-).

XV - Liens

Delphi 2006 : Nouveautés de l'IDE
Présentation de Borland Developper Studio 2006 le remplaçant de C++Builder

Fiche produit
Borland Delphi 2006
Nouveau gestionnaire mémoire sous Delphi 2006 : FastMM4
Discussion autour de LINQ (Language Integrated Query) et Delphi