Types structurés

De RAD Studio (Français)

Remonter à Types de données, variables et constantes - Index

Les instances d'un type structuré contiennent plusieurs valeurs. Les types structurés sont les types ensemble, tableau, enregistrement, fichier, classe, référence de classe et interface. A l'exception des ensembles qui contiennent uniquement des valeurs scalaires, les types structurés peuvent contenir d'autres types structurés ; un type peut avoir un niveau illimité de structuration.

Par défaut, les valeurs d'un type structuré sont alignées sur des limites de mot ou de double-mot afin de disposer d'un accès plus rapide. Quand vous déclarez un type structuré, vous pouvez spécifier le mot réservé packed pour implémenter le stockage compressé des données. Par exemple, type TNumbers = packed array [1..100] of Real;

L'utilisation de packed ralentit l'accès aux données et, dans le cas d'un tableau de caractères, affecte la compatibilité des types (pour plus d'informations, voir Gestion de la mémoire).

Cette rubrique traite les types structurés suivants :

  • Ensembles
  • Tableaux, y compris les tableaux statiques et dynamiques
  • Enregistrements
  • Types de fichiers

Sommaire

Ensembles

Un ensemble est une collection de valeurs ayant le même type scalaire. Les valeurs n'ont pas d'ordre intrinsèque, une même valeur ne peut donc pas apparaître deux fois dans un ensemble.

L'étendue d'un type ensemble est l'ensemble puissance d'un type scalaire particulier, appelé le type de base ; c'est-à-dire que les valeurs possibles du type ensemble sont tous les sous-ensembles du type de base, y compris l'ensemble vide. Le type de base ne peut avoir plus de 256 valeurs possibles et leur rang doit être compris entre 0 et 255. Toute construction de la forme :

set of baseType

baseType est un type ordinal approprié, identifie un type ensemble.

En raison des limitations de taille des types de base, les types ensemble sont généralement définis avec des intervalles. Par exemple, les déclarations :

type
  TSomeInts = 1..250;
  TIntSet = set of TSomeInts;

créent un type d'ensemble appelé TIntSet dont les valeurs sont des collections d'entiers dans l'intervalle 1 à 250. Le même résultat est obtenu avec :

type TIntSet = set of 1..250;

Etant donné cette déclaration, vous pouvez créer un ensemble de la manière suivante :

var Set1, Set2: TIntSet;
  ...
Set1 := [1, 3, 5, 7, 9];
Set2 := [2, 4, 6, 8, 10]

Vous pouvez aussi utiliser directement la construction set of ... dans les déclarations de variables :

var MySet: set of 'a'..'z';
  ...
MySet := ['a','b','c'];

Voici d'autres exemples de type ensemble :

set of Byte
set of (Club, Diamond, Heart, Spade)
set of Char;

L'opérateur in teste l'appartenance à l'ensemble :

if 'a' in MySet then ... { do something } ;

Chaque type ensemble peut contenir l'ensemble vide, qui est désigné par []. Pour de plus amples informations sur les ensembles, voir "Définir les constructeurs" et "Définir les opérateurs" dans Types de données, variables et constantes.

Tableaux

Un tableau représente une collection indicée d'éléments de même type (appelé le type de base). Comme chaque élément a un indice unique, les tableaux (à la différences des ensembles) peuvent, sans ambiguïtés , contenir plusieurs fois la même valeur. Il est possible d'allouer des tableaux de manière statique ou dynamique.

Tableaux statiques

Les tableaux statiques sont désignés par des constructions de la forme :

array[indexType1, ..., indexTypen] of baseType;

où chaque indexType est un type ordinal dont l'étendue ne doit pas dépasser 2 Go. Comme les indexTypes indicent le tableau, le nombre d'éléments que le tableau peut contenir est limité par le produit des tailles des indexTypes. Pratiquement, les indexTypes sont en général des intervalles d'entiers.

Dans le cas le plus simple d'un tableau à une dimension, il n'y a qu'un seul indexType. Par exemple :

var MyArray: array [1..100] of Char;

déclare une variable appelée MyArray qui contient un tableau de 100 valeurs caractère. Etant donné cette déclaration, MyArray[3] désigne le troisième caractère de MyArray. Si vous créez un tableau statique sans affecter de valeurs à tous ses éléments, les éléments inutilisés sont néanmoins alloués et contiennent des données aléatoires ; ils sont identiques à des variables non initialisées.

Un tableau à plusieurs dimensions est un tableau de tableaux. Par exemple :

type TMatrix = array[1..10] of array[1..50] of Real;

est l'équivalent de :

type TMatrix = array[1..10, 1..50] of Real;

Quelle que soit la manière dont TMatrix est déclaré, ce type représente un tableau de 500 valeurs réelles. Une variable MyMatrix de type TMatrix peut être indicée comme ceci : MyMatrix[2,45]; ou comme cela : MyMatrix[2][45]. De même :

packed array[Boolean, 1..10, TShoeSize] of Integer;

est l'équivalent de :

packed array[Boolean] of packed array[1..10] of packed array[TShoeSize] of Integer;

Les fonctions standard Low et High acceptent les variables et les identificateurs de type tableau. Elles renvoient les bornes basse et haute du premier indice du type. La fonction standard Length renvoie le nombre d'éléments dans la première dimension du tableau.

Un tableau à une dimension compacté statique de valeurs Char est appelé une chaîne compactée. Les types chaîne compactée sont compatibles avec les types chaîne et avec les autres types de chaîne compactée ayant le même nombre d'éléments. Voir Compatibilité et identité de types.

Un type tableau de la forme array[0..x] of Char est appelé un tableau de caractères à indice de base zéro. Ces tableaux sont utilisés pour stocker des chaînes à zéro terminal et sont compatibles avec les valeurs PChar. Voir "Manipulation des chaînes à zéro terminal" dans Types chaîne.

Tableaux dynamiques

Les tableaux dynamiques n'ont pas de taille ni de longueur fixe. La mémoire d'un tableau dynamique est réallouée quand vous assignez une valeur au tableau ou le transmettez à la procédure SetLength. Les types de tableau dynamique sont désignés par des constructions de la forme :

array of baseType

Par exemple :

var MyFlexibleArray: array of Real;

déclare un tableau dynamique de réels à une dimension. La déclaration n'alloue pas de mémoire à MyFlexibleArray. Pour créer le tableau en mémoire, appelez SetLength. Par exemple, étant donné la déclaration suivante :

SetLength(MyFlexibleArray, 20);

alloue un tableau de 20 réels indicés de 0 à 19. Une méthode alternative d'allocation de mémoire pour les tableaux dynamiques consiste à invoquer le constructeur de tableaux :

type
  TMyFlexibleArray = array of Integer;

begin
  MyFlexibleArray := TMyFlexibleArray.Create(1, 2, 3 {...});
end;

qui alloue de la mémoire pour trois éléments et affecte à chaque élément la valeur donnée.

Les tableaux dynamiques sont toujours indicés par des entiers, en commençant toujours par 0.

Les variables tableau dynamique sont implicitement des pointeurs et sont gérés par la même technique de comptage de références que celle utilisée pour les chaînes longues. Pour libérer un tableau dynamique, affectez nil à une variable qui référence le tableau ou transmettez la variable à Finalize ; ces deux méthodes libèrent le tableau dans la mesure où il n'y a pas d'autres références le désignant. Les tableaux dynamiques sont automatiquement libérés lorsque leur compte de références atteint zéro. Les tableaux dynamiques de longueur 0 ont la valeur nil. N'appliquez pas l'opérateur de déréférencement (^) à une variable tableau dynamique et ne la transmettez pas aux procédures New et Dispose.

Si X et Y sont des variables du même type de tableau dynamique, X := Y fait pointer X sur le même tableau que Y. Il n'est pas nécessaire d'allouer de la mémoire à X avant d'effectuer cette opération. A la différence des chaînes ou des tableaux statiques, copy-on-write n'est pas utilisé et les tableaux dynamiques ne sont pas copiés quand ils vont être modifiés. Donc, après l'exécution du code suivant :

var
  A, B: array of Integer;
  begin
    SetLength(A, 1);
    A[0] := 1;
    B := A;
    B[0] := 2;
  end;

la valeur de A[0] est 2. Si A et B étaient des tableaux statiques, A[0] vaudrait toujours 1.

L'affectation d'un indice d'un tableau dynamique (par exemple, MyFlexibleArray[2] := 7) ne réalloue pas le tableau. Les indices hors des bornes ne sont pas détectés à la compilation.

En revanche, pour faire une copie indépendante d'un tableau dynamique, vous devez utiliser la fonction Copy globale :

var
  A, B: array of Integer;
begin
  SetLength(A, 1);
  A[0] := 1;
  B := Copy(A);
  B[0] := 2; { B[0] <> A[0] }
end;

Quand des variables tableau dynamique sont comparées, les références sont comparées et non pas la valeur des tableaux. Donc, après l'exécution du code :

var
  A, B: array of Integer;
begin
   SetLength(A, 1);
   SetLength(B, 1);
   A[0] := 2;
   B[0] := 2;
end;

A = B renvoie False mais A[0] = B[0] renvoie True.

Pour tronquer un tableau dynamique, transmettez-le à SetLength ou à Copy, et réaffectez le résultat à la variable tableau. La procédure SetLength est généralement plus rapide. Si, par exemple, A est un tableau dynamique, A := SetLength(A, 0, 20) retranche tout sauf les vingt premiers éléments de A.

Une fois un tableau dynamique alloué, vous pouvez le transmettre aux fonctions standard Length, High et Low. Length renvoie le nombre d'éléments du tableau, High renvoie l'indice le plus élevé du tableau (c'est-à-dire Length - 1) et Low renvoie 0. Dans le cas d'un tableau de longueur nulle, High renvoie -1 (avec cette anomalie que High < Low).

Remarque : Dans les déclarations de certaines procédures et fonctions, les paramètres tableau sont représentés sous la forme array of baseType, sans spécifier le type d'indice. Par exemple, function CheckStrings(A: array of string): Boolean;

Cela indique que la fonction opère sur tous les tableaux du type de base spécifié indépendamment de leur taille, de leurs indices ou de leur allocation statique ou dynamique.

Tableaux dynamiques multi-dimensionnels

Pour déclarer des tableaux dynamiques multi-dimensionnels, utilisez des constructions array of ... répétées. Par exemple :

type TMessageGrid = array of array of string;
var Msgs: TMessageGrid;

déclare un tableau de chaînes à deux dimensions. Pour instancier ce tableau, appelez SetLength avec deux arguments entiers. Par exemple, si I et J sont des variables de valeurs entières :

SetLength(Msgs,I,J);

alloue un tableau de I-fois-J et Msgs[0,0] désigne un élément de ce tableau.

Vous pouvez créer des tableaux dynamiques multidimensionnels qui ne sont pas rectangulaires. Il faut tout d'abord appeler SetLength en lui transmettant les paramètres pour les n premières dimensions du tableau. Par exemple :

var Ints: array of array of Integer;
SetLength(Ints,10);

alloue dix lignes pour Ints mais pas les colonnes. Ultérieurement, vous pouvez allouer les colonnes une à une (en leur donnant des longueurs différentes) ; par exemple :

SetLength(Ints[2], 5);

donne la longueur cinq à la troisième colonne de Ints. A ce stade, même si aucune autre colonne n'a été allouée, vous pouvez affecter des valeurs à la troisième colonne ; par exemple, Ints[2,4] := 6.

L'exemple suivant utilise un tableau dynamique (et la fonction IntToStr déclarée dans l'unité SysUtils) pour créer une matrice triangulaire de chaînes.

var
  A : array of array of string;
  I, J : Integer;
begin
  SetLength(A, 10);
  for I := Low(A) to High(A) do
  begin
    SetLength(A[I], I);
    for J := Low(A[I]) to High(A[I]) do
      A[I,J] := IntToStr(I) + ',' + IntToStr(J) + ' ';
    end;
  end;

Types tableau et affectations

Des tableaux sont compatibles pour l'affectation uniquement s'ils ont le même type. Comme le langage Delphi utilise des équivalences de nom pour les types, le code suivant ne se compile pas :

var
  Int1: array[1..10] of Integer;
  Int2: array[1..10] of Integer;
      ...
  Int1 := Int2;

Pour que l'affectation fonctionne, déclarez les variables comme suit :

var Int1, Int2: array[1..10] of Integer;

ou :

type IntArray = array[1..10] of Integer;
var
   Int1: IntArray;
   Int2: IntArray;

Enregistrements (traditionnels)

Un enregistrement (appelé aussi structure dans certains langages) représente un ensemble de données hétérogènes. Chaque élément est appelé un champ ; la déclaration d'un type enregistrement spécifie le nom et le type de chaque champ. Une déclaration de type enregistrement a la syntaxe suivante :

type recordTypeName = record
  fieldList1: type1;
   ...
  fieldListn: typen;
end

recordTypeName est un identificateur valide et où chaque type désigne un type, et chaque fieldList est un identificateur valide ou une liste d'identificateurs délimitée par des virgules. Le point-virgule final est facultatif.

Par exemple, la déclaration suivante crée un type enregistrement nommé TDateRec.

type
  TDateRec = record
    Year: Integer;
    Month: (Jan, Feb, Mar, Apr, May, Jun,
            Jul, Aug, Sep, Oct, Nov, Dec);
    Day: 1..31;
  end;

Chaque TDateRec contient trois champs : une valeur entière appelée Year, une valeur d'un type énuméré appelé Month et une autre valeur entière comprise entre 1 et 31 appelée Day. Les identificateurs Year, Month et Day sont des noms de champs de TDateRec qui se comportent comme des variables. Néanmoins, la déclaration de type TDateRec n'alloue pas de mémoire pour les champs Year, Month et Day ; la mémoire est allouée quand vous instanciez l'enregistrement, de la manière suivante :

var Record1, Record2: TDateRec;

Cette déclaration de variable crée deux instances de TDateRec, appelées Record1 et Record2.

Vous pouvez accéder aux champs de l'enregistrement en qualifiant le nom de champ avec le nom de l'enregistrement :

Record1.Year := 1904;
Record1.Month := Jun;
Record1.Day := 16;

Ou en utilisant une instruction with :

with Record1 do
begin
  Year := 1904;
  Month := Jun;
  Day := 16;
end;

Vous pouvez alors copier les valeurs des champs de Record1 dans Record2 :

Record2 := Record1;

Comme la portée d'un nom de champ est limitée à l'enregistrement dans lequel il est spécifié, vous n'avez pas à vous préoccuper de conflits entre les noms de champ et des noms de variable.

Au lieu de définir des types d'enregistrement, vous pouvez utiliser directement la construction record ... dans des déclarations de variable :

var S: record
  Name: string;
  Age: Integer;
end;

Cependant, une telle déclaration annule complètement les avantages des enregistrements, à savoir éviter le codage répétitif de groupes de variables identiques. De plus, des enregistrements déclarés séparément avec cette méthode ne sont pas compatibles pour l'affectation même si leur structure est identique.

Modification du compilateur relative à l'instruction With

Une modification a été apportée au compilateur afin de générer des erreurs E2064 dans les blocs de code "with" qui n'invoquaient auparavant aucune erreur. Cela permet d'indiquer les situations où le bloc de code "with" opère sur un enregistrement en lecture seule alors qu'un accès en écriture se produit sur un de ses champs. Auparavant, l'accès en écriture était autorisé sur l'enregistrement, ou il était autorisé sur une copie temporaire de l'enregistrement et l'affectation à l'enregistrement réel échouait en mode silencieux.

Voici un exemple de code qui illustre le problème avec l'ancien compilateur :

var 
  MyClass : TMyClass; 
{ TMyClass } 

procedure TMyClass.InitMyRecord; 
begin 
  fMyRecord.Field1:=1; 
  fMyRecord.Field2:=2; 
end; 

begin 
  MyClass:=TMyClass.Create; 
  MyClass.InitMyRecord; 

  //Compiler eats this ?! 
  with MyClass, MyRecord do 
    //Field1:=2; //#2 

  //Here compiler fails 
  MyClass.MyRecord.Field1:=2; //#3 

  if MyClass.MyRecord.Field1 = 2 then 
    writeLn('FAIL #1 - Read Only property altered'); 

  WriteLn('FAIL #2 - Compile time error expected'); 
  ReadLn; 
end.

Avec l'instruction ci-dessus repérée par #2, l'ancien compilateur ne produit pas d'erreur de compilation et permet l'accès en écriture à Field1. Avec l'instruction #3, une erreur de compilation est générée, mais l'instruction with..do n'est pas utilisée.

Avec le changement de compilateur, voici comment le compilateur intercepte ces problèmes et comment utiliser l'instruction with..do en toute sécurité :

(1) Utilisation de l'instruction WITH et de la propriété d'enregistrement avec des accesseurs.

type 
   TFoo = class(TControl) 
     // : 
     // TControl has BoundsRect property and accessors for read and write. 
     // function GetBoundsRect: TRect; 
     // procedure SetBoundsRect(const Rect: TRect); 
     // property BoundsRect: TRect read GetBoundsRect write SetBoundsRect; 
   end; 
  
var 
   F: TFoo; 
   // : 
   with F.BoundsRect do begin 
     Inc(Left, 10); 
     Inc(Top, 10); 
   end; 
   // Old behavior: F.BoundsRect calls GetBoundsRect and copies 
   // result value in temporary variable. 
   // Inc procedures changes values in it. Original bounds rect 
   // of TFoo instance has never changed. 
   // New behavior: Dcc32 produces two E2064 errors at each Inc procedure. 
   // E2064 Left side cannot be assigned to 
   // Solution: 
   // Create explicit temporary variable. 
var 
   F: TFoo; 
   R: TRect; 
   // : 
   R := F.BoundsRect; 
   with R do begin 
     Inc(Left, 10); 
     Inc(Top, 10); 
   end; 
   if not EmptyRect(F.BoundsRect, R) then 
     F.BoundsRect := R; 
   // Note: Use EmptyRect before call SetBoundsRect 
   // if the cost of SetBoundsRect is high. 

(2) Utilisation de l'instruction WITH et de la propriété d'enregistrement avec des champs.

type 
   TRec = record 
     Data1: Integer; 
     Data2: Integer; 
   end; 
   TFoo = class 
     FRec: TRec; 
     property Rec: TRec read FRec write FRec; 
   end; 
var 
   F: TFoo; 
   // : 
   with F.Rec do begin 
     Inc(Data1, 10); 
     Inc(Data2, 10); 
   end; 
   // Old behavior: F.Rec makes temporary variable to hold pointer to F.FRec. 
   // Inc procedures works correctly as expected. 
   // New behavior: Dcc32 produces two E2064 errors at each Inc procedure. 
   // E2064 Left side cannot be assigned to 
   // Solution: 
   // A) Create explicit temporary variable as (1). or, 
   // B) use F.FRec instead of F.Rec if FRec is visible at the scope. 
   with F.FRec do begin 
     Inc(Data1, 10); 
     Inc(Data2, 10); 
   end; 

(3) Utilisation de l'instruction WITH et de la propriété tableau d'enregistrements avec des champs.

type 
   PFileRec = ^TFileRec; 
   TFileRec = record 
     Data1: Integer; 
     Data2: Integer; 
   end; 
   TFoo = class 
     FList: TList; 
   public 
     function GetFileRec(Index: Integer): TFileRec; 
     property FileRec[Index: Integer]: TFileRec read GetFileRec; 
     procedure Test; 
   end; 
  
function TFoo.GetFileRec(Index: Integer): TFileRec; begin 
   Result := PFileRec(FList[Index])^; 
end; 
  
procedure TFoo.Test; 
begin 
   with FileRec[Index] do begin 
     Inc(Data1, 10); 
     Inc(Data2, 10); 
   end; 
end; 
   // Old behavior: FileRec[Index] makes temporary variable to store 
   // value returned from GetFileRec. 
   // Inc procedures doesn't change original data in FList[Index]^. 
   // New behavior: Dcc32 produces two E2064 errors at each Inc procedure. 
   // E2064 Left side cannot be assigned to 
   // Solution: 
   // A) add new property and accessor to return pointer to data. 
   TFoo = class 
     FList: TList; 
   public 
     function GetFileRec(Index: Integer): TFileRec; 
     property FileRec[Index: Integer]: TFileRec read GetFileRec; 
   private 
     function GetFileRecPtr(Index: Integer): PFileRec; 
   public 
     procedure Test; 
   end; 
 
function TFoo.GetFileRecPtr(Index: Integer): PFileRec; 
begin 
   Result := PFileRec(FList[Index]); 
end; 
 
procedure TFoo.Test; 
begin 
   with FileRecPtr[Index]^ do begin 
     Inc(Data1, 10); 
     Inc(Data2, 10); 
   end; 
end;

Partie variable des enregistrements

Un type enregistrement peut avoir une partie variable qui ressemble à une instruction case. La partie variable doit venir après les autres champs de la déclaration de l'enregistrement.

Pour déclarer un enregistrement avec une partie variable, utilisez la syntaxe suivante :

type recordTypeName = record
  fieldList1: type1;
   ...
  fieldListn: typen;
case tag: ordinalType of
  constantList1: (variant1);
   ...
  constantListn: (variantn);
end;

La première partie de la déclaration, jusqu'au mot réservé case, est identique à celle d'un type d'enregistrement standard. Le reste de la déclaration, du case jusqu'au point-virgule final facultatif, est appelé la partie variable. Dans la partie variable :

  • tag est facultatif, ce peut être tout identificateur valide. Si vous omettez tag, omettez également le caractère deux-points (:) après.
  • ordinalType désigne un type ordinal.
  • Chaque constantList est une constante désignant une valeur du type ordinalType, ou une liste de telles constantes délimitée par des virgules. Une valeur ne doit pas apparaître plus d'une fois dans tous les constantLists.
  • Chaque variant est une liste délimitée par des virgules de déclarations similaires aux constructions fieldList: type de la partie principale du type enregistrement. Donc un variant est de la forme :
fieldList1: type1;
  ...
fieldListn: typen;

où chaque fieldList est un identificateur valide ou une liste d'identificateurs délimitée par des virgules, chaque type désigne un type et le point-virgule final est facultatif. Les types ne peuvent pas être des chaînes longues, des tableaux dynamiques, des types Variant, des interfaces ou des types structurés contenant ces mêmes types. Par contre, ce peut être des pointeurs sur ces types.

Les enregistrements ayant une partie variable ont une syntaxe complexe mais une sémantique très simple. La partie variable d'un enregistrement contient plusieurs variantes qui partagent le même espace mémoire. Vous pouvez lire ou écrire dans tous les champs de toutes les variantes existantes à tout moment ; mais si vous écrivez dans un champ d'une des variantes puis dans un champ d'une autre variante, vous risquez d'écraser vos propres données. Le sélecteur (tag), s'il est spécifié, fonctionne comme un champ supplémentaire (de type ordinalType) dans la partie fixe de l'enregistrement.

Les parties variables ont deux rôles. Tout d'abord, vous pouvez avoir besoin de créer un enregistrement ayant des champs pour différentes sortes de données tout en sachant que vous n'utilisez jamais simultanément tous les champs dans une instance de l'enregistrement. Par exemple :

type
  TEmployee = record
  FirstName, LastName: string[40];
  BirthDate: TDate;
  case Salaried: Boolean of
    True: (AnnualSalary: Currency);
    False: (HourlyWage: Currency);
end;

L'idée ici est que chaque employé a soit un salaire soit un taux horaire, mais pas les deux. Donc, quand vous créez une instance de TEmployee, il n'y a pas de raison pour allouer de la mémoire pour les deux champs. Dans ce cas, la seule différence entre les variantes, c'est le nom du champ. Mais dans d'autres cas, les champs peuvent être de types différents. Voici des exemples plus compliqués :

type
  TPerson = record
  FirstName, LastName: string[40];
  BirthDate: TDate;
  case Citizen: Boolean of
    True: (Birthplace: string[40]);
    False: (Country: string[20];
            EntryPort: string[20];
            EntryDate, ExitDate: TDate);
  end;

type
  TShapeList = (Rectangle, Triangle, Circle, Ellipse, Other);
  TFigure = record
    case TShapeList of
      Rectangle: (Height, Width: Real);
      Triangle: (Side1, Side2, Angle: Real);
      Circle: (Radius: Real);
      Ellipse, Other: ();
  end;

Pour chaque instance d'enregistrement, le compilateur alloue assez de mémoire pour contenir tous les champs de la variante la plus volumineuse. Le sélecteur optionnel et les constantLists (comme Rectangle, Triangle, etc dans le dernier exemple) ne jouent aucun rôle dans la manière dont le compilateur gère les champs ; ils ne sont là que comme commodité pour le programmeur.

L'autre raison d'utiliser des parties variables, c'est qu'elles vous permettent de traiter les mêmes données comme si elles étaient de différents types, et ce même dans les cas où le compilateur n'autorise pas les transtypages. Si par exemple, vous avez un Real sur 64 bits comme premier champ dans une variante et un Integer sur 32 bits comme premier champ d'une autre variante, vous pouvez alors affecter une valeur au champ Real puis en lire les 32 premiers bits comme valeur du champ Integer (et par exemple les transmettre à une fonction nécessitant un paramètre entier).

Enregistrements (avancés)

En plus des types d'enregistrements traditionnels, le langage Delphi autorise des types d'enregistrements plus complexes, similaires à des classes. En plus des champs, les enregistrements peuvent avoir des propriétés et des méthodes (incluant les constructeurs), des propriétés de classe, des méthodes de classe, des champs de classe et des types imbriqués. Pour de plus amples informations sur ces sujets, voir Classes et objets dans la documentation. Voici un exemple de définition de type enregistrement avec une fonctionnalité de type classe.

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;

Bien que les enregistrements puissent maintenant partager l'essentiel de la fonctionnalité des classes, il existe certaines différences importantes entre les classes et les enregistrements.

  • Les enregistrements ne prennent pas en charge l'héritage.
  • Les enregistrements peuvent contenir une partie variable alors que les classes ne le peuvent pas.
  • Les enregistrements sont des types valeur, copiés par affectation, transmis par valeur, et alloués sur la pile à moins qu'ils ne soient déclarés globalement ou alloués explicitement au moyen des fonctions New et Dispose. Les classes sont des types référence ; elles ne sont pas copiées par affectations, elles sont transmises par référence et sont allouées sur le tas.
  • Les enregistrements permettent la surcharge des opérateurs sur la plate-forme Win32 . Toutefois les classes ne permettent pas la surcharge des opérateurs.
  • Les enregistrements sont construits automatiquement, en utilisant un constructeur par défaut sans argument, alors que les classes doivent être construites explicitement. Comme les enregistrements ont un constructeur par défaut sans argument, tous les constructeurs d'enregistrement définis par l'utilisateur doivent avoir un ou plusieurs paramètres.
  • Les types enregistrement ne peuvent pas avoir de destructeurs.
  • Les méthodes virtuelles (celles spécifiées par les mots-clés virtual, dynamic et message) ne peuvent pas être utilisées dans les types enregistrement.
  • A l'inverse des classes, les types d'enregistrements de la plate-forme Win32 ne peuvent pas implémenter les interfaces.

Types fichiers (Win32)

Les types fichiers, uniquement disponibles sur la plate-forme Win32, sont des séquences d'éléments du même type. Les routines standard d'Entrées/Sorties utilisent les types prédéfinis TextFile et Text qui représentent un fichier contenant des caractères organisés en lignes. Pour de plus amples informations sur les entrées et sorties de fichier, voir Routines standard et Entrées/Sorties sous la section "Entrées et sorties de fichier".

Pour déclarer un type fichier, utilisez la syntaxe :

type fileTypeName = file of type

fileTypeName est un identificateur valide et "type" un type de taille fixe. Les types pointeur, implicites ou explicites ne sont pas permis. Un fichier ne peut donc pas contenir des tableaux dynamiques, des chaînes longues, des classes, des objets, des pointeurs, des variants, d'autres fichiers ou des types structurés en contenant.

Par exemple :

type
   PhoneEntry = record
     FirstName, LastName: string[20];
     PhoneNumber: string[15];
     Listed: Boolean;
   end;
   PhoneList = file of PhoneEntry;

déclare un type fichier pour enregistrer des noms et des numéros de téléphone.

Vous pouvez également utiliser directement la construction file of ... dans une déclaration de variable. Par exemple,

var List1: file of PhoneEntry;

Le mot file seul indique un fichier sans type :

var DataFile: file;

Pour de plus amples informations, voir "Fichiers sans type" dans Routines standard et Entrées/Sorties.

Les fichiers ne sont pas autorisés dans les tableaux ou les enregistrements.

Voir aussi

Autres langues