Paramètres (Delphi)

De RAD Studio
Aller à : navigation, rechercher

Remonter à Procédures et fonctions - Index


Cette rubrique traite les sujets suivants :

  • Sémantique des paramètres
  • Paramètres chaîne
  • Paramètres tableau
  • Paramètres par défaut

A propos des paramètres

La plupart des en-têtes de procédure et de fonction comportent une liste de paramètres. Par exemple, dans l'en-tête :

function Power(X: Real; Y: Integer): Real;

la liste de paramètres est (X: Real; Y: Integer).

Une liste de paramètres est une suite placée entre parenthèses de déclarations de paramètres séparées par des points-virgules. Chaque déclaration est une liste séparée par des virgules de noms de paramètres suivi le plus souvent du symbole deux-points et d'un identificateur de type et, dans certains cas, du symbole = et d'une valeur par défaut. Les noms de paramètre doivent être des identificateurs valides. Chaque déclaration peut être précédée par var, const ou out. Exemples :

(X, Y: Real)
(var S: string; X: Integer)
(HWnd: Integer; Text, Caption: PChar; Flags: Integer)
(const P; I: Integer)

La liste des paramètres spécifie le nombre, l'ordre et le type des paramètres qui doivent être transmis à la routine lors de son appel. Si une routine n'utilise aucun paramètre, omettez dans sa déclaration la liste d'identificateurs et les parenthèses :

procedure UpdateRecords;
begin
  ...
end;

A l'intérieur du corps de la procédure ou de la fonction, les noms de paramètres (X et Y dans le premier exemple) peuvent être utilisés comme des variables locales. Ne redéclarez pas le nom des paramètres dans la section des déclarations locales du corps de la routine.

Sémantique des paramètres

Les paramètres peuvent être catégorisés de différentes manières :

  • Chaque paramètre est classé comme paramètre valeur, variable, constante ou de sortie. Les paramètres sont par défaut des paramètres valeur ; les mots réservés var, const et out indiquent, respectivement des paramètres variable, constante et de sortie.
  • Les paramètres valeur sont toujours typés, alors que les paramètres constante, variable et de sortie peuvent être typés ou sans type.
  • Des règles spéciales s'appliquent aux paramètres tableau.

Les fichiers et les instances de types structurés contenant des fichiers ne peuvent être transmis que comme paramètres variable (var).

Paramètres valeur et paramètres variable

La plupart des paramètres sont des paramètres valeur (c'est le cas par défaut) ou des paramètres variable (var). Les paramètres valeur sont transmis par valeur alors que les paramètres variable sont transmis par adresse. Pour saisir la différence, examinez les fonctions suivantes :

function DoubleByValue(X: Integer): Integer;   // X is a value parameter
begin
  X := X * 2;
  Result := X;
end;
 
function DoubleByRef(var X: Integer): Integer;  // X is a variable parameter
begin
  X := X * 2;
  Result := X;
end;

Ces fonctions renvoient le même résultat, mais seule la deuxième (DoubleByRef) peut modifier la valeur d'une variable qui lui est transmise. Si les deux fonctions sont appelées de la manière suivante

var
  I, J, V, W: Integer;
begin
  I := 4;
  V := 4;
  J := DoubleByValue(I);   // J = 8, I = 4
  W := DoubleByRef(V);     // W = 8, V = 8
end;

Après l'exécution de ce code, la variable I, transmise à DoubleByValue, garde la même valeur que celle qui lui a été assignée initialement. Mais la variable V, qui a été transmise à DoubleByRef a une valeur différente.

Un paramètre valeur se comporte comme une variable locale qui est initialisée avec la valeur transmise à l'appel de la routine. Si vous transmettez une variable comme paramètre valeur, la routine en crée une copie et les modifications apportées à la copie sont sans effet sur la variable d'origine et sont perdues quand l'exécution du programme revient à l'appel de la routine.

Un paramètre variable se comporte, lui, comme un pointeur et non comme une copie. Les modifications apportées aux paramètres dans le corps de la routine sont conservées lorsque l'exécution du programme revient à l'appel de la routine et que le nom du paramètre est hors de portée.

Il n'y a pas de copie effectuée même quand la même variable est transmise à plusieurs paramètres var. Ce comportement est mis en évidence par l'exemple suivant :

procedure AddOne(var X, Y: Integer);
begin
  X := X + 1;
  Y := Y + 1;
end;

var I: Integer;
begin
  I := 1;
  AddOne(I, I);
end;

Après l'exécution de ce code, I a la valeur 3.

Si la déclaration d'une routine spécifie un paramètre var, vous devez, lors de l'appel de la routine, lui transmettre une expression assignable (c'est-à-dire une variable, une constante typée (dans l'état {$J+}), un pointeur déréférencé, un champ ou une variable indexée). Dans nos exemples précédents, DoubleByRef(7) produit une erreur, bien que DoubleByValue(7) soit légal.

Les index et les pointeurs de référence transmis dans des paramètres var, par exemple DoubleByRef(MyArray[I]), ne sont évalués qu'une seule fois, avant l'exécution de la routine.

Paramètres constante

Un paramètre constante (const) est semblable à une constante locale ou à une variable en lecture seule. Les paramètres constante sont semblables aux paramètres par valeur à cette différence qu'il n'est pas possible d'assigner une valeur à un paramètre constante dans le corps de la routine, ni de le transmettre comme paramètre var à une autre routine. Par contre, si vous transmettez une référence d'objet comme paramètre constante, il est quand même possible de modifier les propriétés de l'objet.

L'utilisation de const permet au compilateur d'optimiser le code pour les paramètres de type structuré ou chaîne. Cela évite aussi de transmettre involontairement par adresse le paramètre à une autre routine.

Voici, par exemple, l'en-tête de la fonction CompareStr de l'unité SysUtils

function CompareStr(const S1, S2: string): Integer;

Comme S1 et S2 ne sont pas modifiés dans le corps de CompareStr, ils peuvent être déclarés en tant que paramètres constante.

Les paramètres constante peuvent être transmis à la fonction par valeur ou par référence, selon le compilateur utilisé. Pour obliger le compilateur à transmettre un paramètre constante par référence, vous pouvez utiliser le décorateur [Ref] avec le mot clé const.

L'exemple suivant illustre comment spécifier le décorateur [Ref] avant ou après le mot clé const :

function FunctionName(const [Ref] parameter1: Class1Name; [Ref] const parameter2: Class2Name);

Paramètres de sortie

Un paramètre de sortie (out) est transmis par adresse comme un paramètre variable. Mais avec un paramètre out, la valeur initiale de la variable référencée n'est pas prise en compte par la routine à laquelle elle est transmise. Le paramètre out n'est utilisé qu'en sortie ; il indique simplement à la routine où placer la valeur en sortie sans spécifier de valeur en entrée.

Soit, par exemple, l'en-tête de procédure suivant :

procedure GetInfo(out Info: SomeRecordType);

Quand vous appelez GetInfo, vous devez lui transmettre une variable de type SomeRecordType

var MyRecord: SomeRecordType;
   ...
GetInfo(MyRecord);

Mais vous n'utilisez pas MyRecord pour transmettre des données à la procédure GetInfo ; MyRecord sert simplement de conteneur où GetInfo stocke les informations qu'elle génère. L'appel de GetInfo libère immédiatement la mémoire utilisée par MyRecord, avant que le contrôle du programme ne passe à la procédure.

               Les paramètres out sont fréquemment utilisés avec les modèles d'objets distribués comme COM. De plus, vous devez utiliser des paramètres out quand vous passez une variable non initialisée à une fonction ou une procédure.

Paramètres non typés

Il est possible d'omettre la spécification de type quand vous déclarez des paramètres var, const et out. Les paramètres valeur doivent être typés. Par exemple :

procedure TakeAnything(const C);

déclare une procédure appelée TakeAnything qui accepte un paramètre de type quelconque. Lorsque vous appelez une telle routine, vous ne pouvez pas lui passer un nombre ou une constante numérique non typée.

Dans le corps d'une procédure ou d'une fonction, les paramètres non typés sont incompatibles avec chaque type. Pour agir sur un paramètre non typé, vous devez le transtyper. En général, le compilateur ne peut pas vérifier si les opérations effectuées sur les paramètres non typés sont légales.

L'exemple suivant utilise des paramètres non typés dans une fonction appelée Equal qui compare le nombre spécifié d'octets de deux variables quelconques

function Equal(var Source, Dest; Size: Integer): Boolean;
type
  TBytes = array[0..MaxInt - 1] of Byte;
var
  N : Integer;
begin
  N := 0;
  while (N < Size) and (TBytes(Dest)[N] = TBytes(Source)[N]) do
    Inc(N);
  Equal := N = Size;
end;

Etant donné les déclarations suivantes :

type
  TVector = array[1..10] of Integer;
  TPoint = record
    X, Y: Integer;  // Integer occupies 4 bytes. Therefore 8 bytes in a whole
  end;
var
  Vec1, Vec2: TVector;
  N: Integer;
  P: TPoint;

vous pouvez faire les appels suivants de la fonction Equal

Equal(Vec1, Vec2, SizeOf(TVector));      // compare Vec1 to Vec2
Equal(Vec1, Vec2, SizeOf(Integer) * N);  // compare first N 
                                         // elements of Vec1 and Vec2
Equal(Vec1[1], Vec1[6], SizeOf(Integer) * 5); // compare first 5 to
                                              // last 5 elements of Vec1
Equal(Vec1[1], P, 8);                    // compare Vec1[1] to P.X and Vec1[2] to P.Y
                                         // each Vec1[x] is integer and occupies 4 bytes
Remarque: FreeAndNil n'accepte pas les paramètres var non typés.

Par exemple, pour transtyper TObject, Obj doit être typé ainsi :

procedure FreeAndNil(var Obj: TObject);

Paramètres chaîne

Lorsque vous déclarez des routines qui acceptent des paramètres chaîne courte, vous ne pouvez pas inclure des spécificateurs de longueur dans les déclarations de paramètres. Ainsi, la déclaration suivante provoque une erreur de compilation :

procedure Check(S: string[20]);   // syntax error

Mais la déclaration suivante est valide :

type TString20 = string[20];
procedure Check(S: TString20);

L'identificateur spécial OpenString peut être utilisé pour déclarer des routines qui prennent des paramètres chaîne courte de longueur variable :

procedure Check(S: OpenString);

Lorsque les directives de compilation {$H} et {$P+} sont toutes deux effectives, le mot réservé string est équivalent à OpenString dans les déclarations de paramètres.

Les chaînes courtes, OpenString, $H et $P sont prises en charge uniquement dans un souci de compatibilité descendante. Dans du nouveau code, vous pouvez ignorer ces considérations en utilisant des chaînes longues.

Paramètres tableau

Quand vous déclarez des routines utilisant des paramètres tableau, vous ne pouvez pas introduire de spécificateur de type d'index dans la déclaration du paramètre. En effet, la déclaration :

procedure Sort(A: array[1..10] of Integer)  // syntax error

génère une erreur de compilation. Mais :

type TDigits = array[1..10] of Integer;
procedure Sort(A: TDigits);

est valide. Une autre approche consiste à utiliser des paramètres tableau ouvert.

Puisque le langage Delphi n'implémente pas la sémantique des valeurs pour les tableaux dynamiques, les paramètres 'value' des routines ne représentent pas un copie complète du tableau dynamique. Dans cet exemple :

type
  TDynamicArray = array of Integer;
  procedure p(Value: TDynamicArray);
    begin
      Value[0] := 1;
    end;

  procedure Run;
    var
      a: TDynamicArray;
    begin
      SetLength(a, 1);
      a[0] := 0;
      p(a);
      Writeln(a[0]); // Prints '1'
    end;

Notez que l'assignation de Value[0] dans la routine p modifiera le contenu du tableau dynamique de l'appelant, bien que Value soit un paramètre par valeur. Si une copie complète du tableau dynamique est requise, utilisez la procédure standard Copy pour créer une copie des valeurs du tableau dynamique.

Paramètres tableau ouvert

Les paramètres tableau ouvert permettent de transmettre des tableaux de tailles différentes à la même routine. Pour définir une routine ayant un paramètre tableau ouvert, utilisez la syntaxe array of type (au lieu de array[X..Y] of type) dans la déclaration du paramètre. Par exemple

function Find(A: array of Char): Integer;

déclare une fonction appelée Find qui prend comme paramètre un tableau de caractères de taille quelconque et renvoie un entier.

Remarque : La syntaxe des paramètres tableau ouvert ressemble à celle des types de tableau dynamique mais elle ne signifie pas la même chose. L'exemple précédent crée une fonction qui accepte comme paramètre tout tableau d'éléments Char, notamment les tableaux dynamiques, mais pas uniquement eux. Pour déclarer des paramètres qui doivent être des tableaux dynamiques, vous devez spécifier un identificateur de type :

type TDynamicCharArray = array of Char;
function Find(A: TDynamicCharArray): Integer;

Dans le corps d'une routine, les paramètres tableau ouvert sont régis par les règles suivantes.

  • Ce sont toujours des tableaux d'index de base zéro. Le premier élément est 0, le second 1 et ainsi de suite. Les fonctions standard Low et High renvoient, respectivement, 0 et Length - 1. La fonction SizeOf renvoie la taille du tableau réellement transmis à la routine.
  • On ne peut y accéder qu'élément par élément. Les assignations à un paramètre tableau ouvert entier sont incorrectes.
  • Ils ne peuvent être transmis à d'autres fonctions ou procédures que comme paramètres tableau ouvert ou comme paramètres var non typés. Ils ne peuvent être transmis à SetLength.
  • Au lieu d'un tableau, vous pouvez transmettre une variable du type de base du paramètre tableau ouvert. Elle est traitée comme un tableau de longueur 1.

Quand vous transmettez un tableau comme paramètre tableau ouvert par valeur, le compilateur crée une copie locale du tableau à l'intérieur du cadre de pile de la routine. Attention : vous ne devez pas provoquer de débordement du cadre de pile en transmettant de gros tableaux.

Les exemples suivants utilisent des paramètres tableau ouvert pour définir une procédure Clear qui assigne la valeur zéro à chaque élément d'un tableau de réels et une fonction Sum qui calcule la somme de tous les éléments d'un tableau de réels :

procedure Clear(var A: array of Real);
var
   I: Integer;
begin
   for I := 0 to High(A) do A[I] := 0;
end;

function Sum(const A: array of Real): Real;
var
  I: Integer;
  S: Real;
begin
  S := 0;
  for I := 0 to High(A) do S := S + A[I];
  Sum := S;
end;

Quand vous appelez des routines qui utilisent des paramètres tableau ouvert, vous pouvez leur transmettre des constructeurs de tableaux ouverts.

Paramètres tableau ouvert variant

Les paramètres tableau ouvert variant permettent de transmettre un tableau d'expressions de types différents à une seule procédure ou fonction. Pour définir une routine avec un paramètre tableau ouvert variant, spécifiez array of const comme type du paramètre. Ainsi :

procedure DoSomething(A: array of const);

déclare une procédure appelée DoSomething qui peut agir sur des tableaux de données hétérogènes.

La construction array of const est équivalente à array of TVarRec. System.TVarRec, qui est déclarée dans l'unité System représente un enregistrement avec une partie variable qui peut contenir des valeurs de type entier, booléen, caractère, réel, chaîne, pointeur, classe, référence de classe, interface et variant. Le champ VType de TVarRec indique le type de chaque élément du tableau. Certains types sont transmis comme pointeur et non comme valeur ; en particulier les chaînes sont transmises comme Pointer et doivent être transtypées en string.

L'exemple Win32 suivant utilise un paramètre tableau ouvert variant dans une fonction qui crée une représentation sous forme de chaîne de chaque élément transmis et concatène les résultats dans une seule chaîne. Les routines de manipulation de chaînes appelées dans cette fonction sont définies dans l'unité SysUtils :

function MakeStr(const Args: array of const): string;
var
  I: Integer;
begin
  Result := '';
  for I := 0 to High(Args) do
     with Args[I] do
        case VType of
          vtInteger:  Result := Result + IntToStr(VInteger);
          vtBoolean:  Result := Result + BoolToStr(VBoolean);
          vtChar:     Result := Result + VChar;
          vtExtended: Result := Result + FloatToStr(VExtended^);
          vtString:   Result := Result + VString^;
          vtPChar:    Result := Result + VPChar;
          vtObject:   Result := Result + VObject.ClassName;
          vtClass:    Result := Result + VClass.ClassName;
          vtAnsiString:  Result := Result + string(VAnsiString);
          vtUnicodeString:  Result := Result + string(VUnicodeString);
          vtCurrency:    Result := Result + CurrToStr(VCurrency^);
          vtVariant:     Result := Result + string(VVariant^);
          vtInt64:       Result := Result + IntToStr(VInt64^);
  end;
end;

Il est possible d’appeler cette fonction en utilisant un constructeur de tableau ouvert. Par exemple :

MakeStr(['test', 100, ' ', True, 3.14159, TForm])

renvoie la chaîne 'test100 T3.14159TForm'.

Paramètres par défaut

Il est possible de définir la valeur par défaut de paramètres dans l'en-tête d'une procédure ou d'une fonction. Les valeurs par défaut sont autorisées uniquement pour les paramètres const typés et les paramètres valeur. Pour spécifier une valeur par défaut, terminez la déclaration du paramètre par le symbole = suivi d'une expression constante compatible pour l'assignation avec le type du paramètre.

Par exemple, soit la déclaration :

procedure FillArray(A: array of Integer; Value: Integer = 0);

les appels de procédure suivants sont équivalents :

FillArray(MyArray);
FillArray(MyArray, 0);

Une déclaration de paramètres multiples ne peut spécifier de valeur par défaut. Ainsi, tandis que la déclaration suivante est correcte :

function MyFunction(X: Real = 3.5; Y: Real = 3.5): Real;

La déclaration suivante n'est pas correcte :

function MyFunction(X, Y: Real = 3.5): Real;  // syntax error

Les paramètres ayant une valeur par défaut doivent se trouver à la fin de la liste des paramètres. C'est-à-dire que tous les paramètres suivant un paramètre ayant une valeur par défaut doivent aussi avoir une valeur par défaut. Ainsi, la déclaration suivante est incorrecte :

procedure MyProcedure(I: Integer = 1; S: string);  // syntax error

Les valeurs par défaut spécifiées dans un type procédure ont la priorité sur celles spécifiées dans la routine réelle. Ainsi, étant donné les déclarations suivantes :

type TResizer = function(X: Real; Y: Real = 1.0): Real;
function Resizer(X: Real; Y: Real = 2.0): Real;
var
  F: TResizer;
  N: Real;

les instructions :

F := Resizer;
F(N);

provoquent le transfert des valeurs (N, 1.0) à Resizer.

Les paramètres par défaut sont limités aux valeurs pouvant être spécifiées par une expression constante. De ce fait, les paramètres de type tableau dynamique, procédure, classe, référence de classe ou interface ne peuvent avoir que nil comme valeur par défaut. Les paramètres de type enregistrement, variant, fichier, tableau statique ou objet ne peuvent pas avoir du tout de valeur par défaut.

Paramètres par défaut et routines surchargées

Si vous utilisez des valeurs par défaut de paramètre dans des routines surchargées, évitez les signatures de paramètre ambiguës. Soit par exemple, le code suivant :

procedure Confused(I: Integer); overload;
   ...
procedure Confused(I: Integer; J: Integer = 0); overload;
   ...
Confused(X);   //  Which procedure is called?

En fait, aucune des deux procédures n'est appelée : Ce code génère une erreur de compilation.

Paramètres par défaut dans les déclarations forward et interface

Si une routine a une déclaration forward ou apparaît dans la section interface d'une unité, vous ne pouvez spécifier la valeur par défaut des paramètres que dans la déclaration forward ou dans la déclaration d'interface. Dans ce cas, les valeurs par défaut peuvent être omises dans la déclaration de définition (l'implémentation), mais si la déclaration de définition comprend des valeurs par défaut, elles doivent correspondre exactement avec celles de la déclaration forward ou de la déclaration d'interface

Voir aussi