IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
logo

FAQ DelphiConsultez toutes les FAQ

Nombre d'auteurs : 124, nombre de questions : 934, dernière mise à jour : 23 octobre 2024  Ajouter une question

 

Cette FAQ a été réalisée à partir des questions fréquemment posées sur les forums Delphi et Delphi et bases de données de www.developpez.com et de l'expérience personnelle des auteurs.

Nous tenons à souligner que cette FAQ ne garantit en aucun cas que les informations qu'elle propose soient correctes. Les auteurs font le maximum, mais l'erreur est humaine. Cette FAQ ne prétend pas non plus être complète. Si vous souhaitez y apporter des corrections ou la compléter, contactez un responsable (lien au bas de cette page).

Nous espérons que cette FAQ saura répondre à vos attentes. Nous vous en souhaitons une bonne lecture.

L'équipe Delphi de Developpez.com.

SommaireLangage (72)
précédent sommaire suivant
 

Schématiquement à la question qui suis-je ?, le mot clé Self répond moi-même en renvoyant la référence de l'instance en cours d'utilisation au sein d'une méthode de sa classe. Self permet donc de connaître l'instance en cours et dans certains cas de lever toute ambiguité.

Par exemple ici :

Code delphi : Sélectionner tout
1
2
3
4
5
procedure TObject.Free;  
begin  
  if Self <> nil then  
    Destroy;  
end;
On souhaite savoir si l'objet en cours est instancié ou pas (nil) avant d'appeler le destructeur.
Self est aussi une référence sur une classe et peut donc ne pas être affectée (MonObjet:=Nil). Cette méthode étant statique son appel ne pose pas de problème dans ce cas.

Et là :
Code delphi : Sélectionner tout
1
2
3
with TPaintBox(Sender).Canvas.Brush do  
  begin  
    Color := Self.Color;
Il y a une ambiguïté du à la présence de la propriété color commune à l'instance en cours d'utilisation et à un objet imbriqué (Canvas.Brush).
Self permet ici de savoir exactement de quelle propriété on parle.

Dans le cas d'une méthode de classe l'utilisation de Self à une signification différente, il s'agit d'une variable de type référence de classe contenant la classe effective de l'objet. Schématiquement dans ce cas le mot clé Self répond à la question De quelle classe suis-je ? en répondant De la classe TMaclasse.

Mis à jour le 20 janvier 2014 Laurent Dardenne Pedro

Supposons que vous souhaitez, pour telle ou telle raison utiliser une Méthode protégée d'un Objet particulier que l'on appellera MaSomme (cela peut être un composant visuel ou non par exemple).

Supposons que notre objet ai été défini comme suit, dans une unité à part :

Code delphi : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
unit UnitSomme;  
  
interface  
  
type  
  TSomme = class  
   private  
     FA,FB: Integer;  
     FResultat: Integer;  
     procedure SetA(const Value: Integer);  
     procedure SetB(const Value: Integer);  
   protected  
     // La fonction que l'on souhaiterais utiliser...  
     function Somme(A,B:Integer):Integer;  
   public  
     constructor Create;  
     destructor Destroy;  
     property A:Integer read FA write SetA;  
     property B:Integer read FB write SetB;  
     property Resultat:Integer read FResultat;  
  end;  
...  
implementation  
...  
function TSomme.Somme(A,B:Integer):Integer;  
begin  
  result:=A+B;  
end;  
  
...  
end.
Alors, habituellement, vous l'utiliseriez comme ceci, par exemple :
Code delphi : Sélectionner tout
1
2
3
4
5
6
var MaSomme:TSomme;  
  ...  
  MaSomme.A:=5;  
  MaSomme.B:=9;  
  Result:=MaSomme.Resultat;  //  
  ...
Peut être auriez-vous préféré utiliser la fonction Factorielle de l'Objet MaFactorielle directement dans votre code, mais cette dernière étant protégée (c'est à dire déclarée dans le bloc protected de la classe TFactorielle), l'accès direct à la méthode de l'objet vous est interdit selon les lois de la programmation orientée objet.

La solution :
L'idée est la suivante :
  • Créer une classe dérivée de la classe TFactorielle, que l'on appellera par exemple TPublicFactorielle , ceci dans l'unité même ou l'on souhaite utiliser la méthode.
  • de Transtyper notre objet MaFactorielle comme suit :

Code delphi : Sélectionner tout
TPublicFactorielle(MaFactorielle)
afin d'accéder la fonction rendue visible.
De cette façon, nous pourrons contourner la protection objet sans pour autant enfreindre les lois de la POO.

Voici l'unité d'une fiche fraîchement créée dans laquelle nous avons ajouté le code ad hoc :
Code delphi : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
unit Unit1;  
  
interface  
  
uses  
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,  Dialogs, unitFactorielle;  
  
type  
  //Notre classe dérivée...  
  //A travers sa définitions, toutes les méthodes protégées deviennent  
  //visibles pour tout le code écrit dans notre unité unit1  
  TPublicSomme=class(TSomme);  
  
  TForm1 = class(TForm)  
  private  
    { Déclarations privées }  
  public  
    { Déclarations publiques }  
    procedure Calcule;  
  end;  
...  
implementation  
...  
procedure TForm1.Calcule;  
var MaSOmme:TSomme;  
begin  
...  
  //On transtype MaSomme afin d'utiliser la méthode rendu visible  
  Result:=TPublicSomme(MaSomme).Somme(A,B);  
....  
end;
Applications :
Dans la FAQ/Sources, nous retrouverons les astuces suivantes qui exploitent ce procédé, ou en dérivent :

Mis à jour le 20 janvier 2014 LadyWasky

Un exemple concret :
On souhaite, par exemple, accéder depuis le code d'une fiche à la variable privée FImageIndex d'un TMenuItem , (n'importe quelle variable privée de n'importe quel objet de n'importe quelle classe aurait pu faire l'affaire).

Solution :
Tout réside dans les possibilités du transtypage, et du fait que les types classes ne sont rien d'autre que des types de pointeurs particuliers.

Tout d'abord, Il faut déclarer la classe TMenuItemPublic de telle façon quelle dérive exactement du même objet que TMenuItem, en l'occurence de TComponent.

Vous remarquerez, que l'on y retrouve exactement les mêmes variables et exactement dans le même ordre que dans la classe TMenuItem, à la différence que ces variables sont toutes déclarées publiques.

Ainsi, on obtient une classe ayant la même structure en mémoire que la classe TMenuItem, ce qui nous autorise alors à transtyper un TMenuItem en TMenuItemPublic et nous permet alors d'accéder à toutes les variables privées de TMenuItem comme si elles avaient été déclarées Public !

Code delphi : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
type  
TMenuItemPublic=class(TComponent)  
  public  
    FCaption: string;  
    FHandle: HMENU;  
    FChecked: Boolean;  
    FEnabled: Boolean;  
    FDefault: Boolean;  
    FAutoHotkeys: TMenuItemAutoFlag;  
    FAutoLineReduction: TMenuItemAutoFlag;  
    FRadioItem: Boolean;  
    FVisible: Boolean;  
    FGroupIndex: Byte;  
    FImageIndex: TImageIndex;  
    FActionLink: TMenuActionLink;  
    FBreak: TMenuBreak;  
    FBitmap: TBitmap;  
    FCommand: Word;  
    FHelpContext: THelpContext;  
    FHint: string;  
    FItems: TList;  
    FShortCut: TShortCut;  
    FParent: TMenuItem;  
    FMerged: TMenuItem;  
    FMergedWith: TMenuItem;  
    FMenu: TMenu;  
    FStreamedRebuild: Boolean;  
    FImageChangeLink: TChangeLink;  
    FSubMenuImages: TCustomImageList;  
    FOnChange: TMenuChangeEvent;  
    FOnClick: TNotifyEvent;  
    FOnDrawItem: TMenuDrawItemEvent;  
    FOnAdvancedDrawItem: TAdvancedMenuDrawItemEvent;  
    FOnMeasureItem: TMenuMeasureItemEvent;  
    FAutoCheck: Boolean;  
  end;
La variable qui nous intéresse en particulier étant FImageIndex, le problème est résolu comme ceci :
Code delphi : Sélectionner tout
1
2
3
4
5
6
var UnMenuItem:TMenuItem;  
begin  
....  
  TMenuItemPublic(UnMenuItem).FImageIndex:=NewIndex;  
....  
end;
Rubriques connexes de la FAQ :

Mis à jour le 20 janvier 2014 LadyWasky

Certains constructeurs de classe peuvent lors de leur exécution générer une exception. Dans ce cas son destructeur est appelé automatiquement.
Cette situation étant gérée le plus souvent par le bloc suivant :

Code delphi : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
Var MonObjet : TUneClasse; 
    ParametreUn : String; 
  
begin 
 Try 
    MonObjet:=TUneClasse.Create(ParametreUn); 
    ...Traitement... 
  Finally 
    FreeAndNil(MonObjet); 
  end; 
end;
Ici malheureusement l'instruction FreeAndNil du bloc Finally peut provoquer une violation d'accés (AV) étant donné que le contenu de la variable MonObjet est indéterminé.
La modification de l'instance concernée dans le destructeur de sa classe n'est pas possible.

Pour éviter cette possible violation d'accés il faut tout simplement initialiser la variable à NIL AVANT l'appel du
constructeur :
Code delphi : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
Var MonObjet : TUneClasse; 
    ParametreUn : String; 
  
begin 
 Try 
   MonObjet:=Nil; 
   MonObjet:=TUneClasse.Create(ParametreUn); 
   ...Traitement... 
  finally 
   FreeAndNil(MonObjet); 
  end; 
end;
soit utiliser une méthode de classe se chargeant d'appeler le constructeur et évitera la duplication du bloc except :
Code delphi : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
Class procedure TUneClasse.CreateAndNilIfExcept(Var AObjet:TUneClasse; ParametreUn:String); 
begin 
 try 
  AObjet:=TUneClasse.Create(ParametreUn); 
 except 
  On Exception do 
   begin 
    AObjet:=Nil; //Evite la violation d'accés 
    Raise;        // On redéclenche l'exception 
   end; 
 end; 
end;
Le code initial devenant :
Code delphi : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
Var MonObjet : TUneClasse; 
    ParametreUn : String; 
  
begin 
 Try 
   TUneClasse.CreateAndNilIfExcept(MonObjet,ParametreUn); 
   ...Traitement... 
  finally 
   FreeAndNil(MonObjet); 
  end; 
end;
Dans ce second cas, la syntaxe with NomDeClasse.Create(...) do est donc à éviter si vous souhaitez gérer cette situation.

Mis à jour le 20 janvier 2014 Laurent Dardenne sjrd

L'utilisation de l'instruction case ... of en Delphi, quoique pratique dans la plupart des cas de programmation peut parfois se révéler inappropriée lorsque le nombre de branchements devient trop grand :

  • Lourdeur du code
  • Vitesse d'exécution désastreuse.

Ce cas peut parfois être rencontré lorsque l'on écrit par exemple :
  • un émulateur : on effectue une action particulière différente pour chaque instruction du microprocesseur.
    Dans ce cas, la vitesse d'exécution est primordiale.
  • un jeu d'aventure :
    • Chaque case d'une carte peut être un type de rencontre différente.
    • Chaque objet possédé par le héros possède un effet différent lorsqu'il est utilisé, chaque arme une attaque particulière, chaque sortilège déclenche un effet particulier.

    Dans ce cas c'est la clarté du code qui permettra le design d'un univers riche en contenu et rebondissements.

Par exemple, ce type de code…
Code delphi : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
procedure TForm1.UneActionADeterminer(Parametre:integer); 
begin 
  ... 
  case Parametre of 
      0:MaProcedure0; 
      1:MaProcedure1; 
      2:MaProcedure2; 
      3:MaProcedure3; 
      4:MaProcedure4; 
      5:MaProcedure5; 
      ..... 
      203:MaProcedure203; 
      //etc... 
  end; 
  ... 
end;
…gagnera en clarté et en célérité si nous pouvions le transformer en :
Code delphi : Sélectionner tout
1
2
3
4
5
6
procedure TForm1.UneActionADeterminer(Parametre:integer); 
begin 
  ... 
  MesProcedures[Parametre];       
  ... 
end;
Nous venons de présenter ici le cas d'un tableau de procédures sans paramètre. Nous allons voir qu'il est tout aussi possible d'utiliser un tableau de fonctions ou bien encore d'utiliser des paramètres.

Pour aboutir à ce résultat, nous devons :
1) Définir un type de variable pouvant contenir un type générique de procédure ou de fonction avec ou sans paramètre (Variables procédurales) :
Il s'agit de définir ici un type de variable un peu particulière destiné à recevoir une procédure ou une fonction. Ce genre de variable s'appelle une variable procédurale. La syntaxe pour déclarer ce type de variable est la suivante :
Code delphi : Sélectionner tout
<Nom du Type> = <procedure ou function>[(<paramètres>)[:<type de resultat>]] [of object;]
(Nota : " of object " signifie que nos procédures ou fonctions seront définies dans une classe comme TForm1 par exemple : elles font partie d'un objet.)
Ainsi aurons nous ce genre de déclarations pour des :
  • Variables de type procedure : avec paramètres (c'est ce genre de déclaration qui est utilisé pour définir des variables de type évènement comme TNotifyEvent, TDrawItemEvent, etc..) :

Code delphi : Sélectionner tout
1
2
type 
  TProcedureAvecParametres=procedure(a,b:integer;s:string) of object;
  • sans paramètre :

Code delphi : Sélectionner tout
1
2
type 
  TProcedureSimple=procedure of object;
Variables de type function avec paramètres :
Code delphi : Sélectionner tout
1
2
type 
  TOperationArithmetique=function(a,b:real):real of object;
  • sans paramètre :

Code delphi : Sélectionner tout
1
2
type 
  TTraiterUnCodeErreur=function:boolean of object;
2) Écrire nos procédures ou fonctions :

L'exemple suivant concerne des fonctions avec paramètres, définies dans une classe (notez le " of object")
Code delphi : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
type 
  
TOperationArithmetique=function(a,b:real):real of object; 
  
TForm1 = class(TForm) 
    ... 
  private 
    { Déclarations privées } 
  public 
    { Déclarations publiques } 
    ... 
    TableauDeFonctions:array[0..3] of TOperationArithmetique; 
    function Addition(a,b:real):real; 
    function Multiplication(a,b:real):real; 
    function Division(a,b:real):real; 
    function Soustraction(a,b:real):real; 
    ... 
  end; 
... 
implementation 
... 
function TForm1.Addition(a,b:real):real; 
begin 
  result:=a+b; 
end; 
  
function TForm1.Multiplication(a,b:real):real; 
begin 
  result:=a*b; 
end;
Les fonctions sont des fonctions globales n'appartenant pas à une classe (disparition du " of object") :
Code delphi : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
type 
  
TOperationArithmetique=function(a,b:real):real; 
  
TForm1 = class(TForm) 
    ... 
  private 
    { Déclarations privées } 
  public 
    { Déclarations publiques } 
    ... 
    TableauDeFonctions:array[0..3] of TOperationArithmetique; 
    ... 
  end; 
  
function Addition(a,b:real):real; 
function Multiplication(a,b:real):real; 
function Division(a,b:real):real; 
function Soustraction(a,b:real):real; 
... 
implementation 
... 
function Addition(a,b:real):real; 
begin 
  result:=a+b; 
end; 
  
function Multiplication(a,b:real):real; 
begin 
  result:=a*b; 
end;
3) …Assigner une procédure à chaque élément du tableau :

Dans cette partie de code, nous allons simplement remplir notre tableau aussi simplement que nous le ferions avec des variables simples comme des entiers ou des chaînes de caractères :
Code delphi : Sélectionner tout
1
2
3
4
5
6
7
procedure TForm1.InitialisationDuTableau 
begin 
  TableauDeFonctions[0]:=Addition; 
  TableauDeFonctions[1]:=Multiplication; 
  ... 
  //etc. 
end;
4) Utiliser notre tableau :

  • Avec des procédures sans paramètre :

Code delphi : Sélectionner tout
1
2
3
4
5
6
7
8
9
procedure TForm1.UneActionADeterminer(Parametre:integer); 
begin 
  //si Parametre est compris entre la borne inférieure et la borne supérieure du tableau de procédures 
  if ((Parametre>=low(TableauDeProcedures)) and (Parametre<=high(TableauDeProcedures)) then 
  //si l'entrée du tableau contient effectivement quelque chose (une procédure) 
  if Assigned(TableauDeProcedures[Parametre]) then 
  //exécution de la procédure ad hoc 
  TableauDeProcedures[Parametre]; 
end;
  • Avec des fonctions acceptant des paramètres :

Code delphi : Sélectionner tout
1
2
3
4
5
6
procedure TForm1.Calcule(OperationChoisie:integer;NombreA,NombreB:real;var resultat:real); 
begin 
   if ((OperationChoisie>=0) and (OperationChoisie<=3)) then 
   if Assigned(TableauDeFonctions[OperationChoisie]) then 
   resultat:=TableauDeFonctions[OperationChoisie](NombreA,NombreB); 
end;
Un exemple complet :
Dans un nouveau projet, nous avons placé un TButton sur notre fiche.
Code delphi : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
unit Unit1; 
  
interface 
  
uses 
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, 
  Dialogs, StdCtrls; 
type 
  //Définition d'un type de procedure simple 
  TProcedureAffichage=procedure of object; 
  
  TForm1 = class(TForm) 
    Button1: TButton; 
    procedure Button1Click(Sender: TObject); 
    procedure FormCreate(Sender: TObject); 
  private 
    { Déclarations privées } 
  public 
    { Déclarations publiques } 
    TableauDeProcedures:array[0..2] of TProcedureAffichage; 
    procedure AffichageShowMessage; 
    procedure AffichageDansLaFiche; 
    procedure AffichageBarreDeTitre; 
  end; 
  
var 
  Form1: TForm1; 
  
implementation 
  
{$R *.dfm} 
  
{ TForm1 } 
  
procedure TForm1.AffichageShowMessage; 
begin 
  showmessage('Procedure n°1'); 
end; 
  
procedure TForm1.AffichageDansLaFiche; 
begin 
  canvas.font.Color:=random($ffffff); 
  canvas.TextOut(10,10,'Procédure n°2'); 
end; 
  
procedure TForm1.AffichageBarreDeTitre; 
var i:integer; 
    oldcaption:string; 
begin 
  oldcaption:=Caption; 
  for i:=0 to 9 do 
  begin 
    caption:='Procedure n°3'; 
    sleep(200); 
    caption:=''; 
    sleep(100); 
  end; 
  caption:=oldcaption; 
end; 
  
//Événement OnClick du Bouton 
procedure TForm1.Button1Click(Sender: TObject); 
var i:integer; 
begin 
  //choix aléatoire d'une procedure d'affichage 
  i:=Random(3); 
  if assigned(TableauDeProcedures<i>) then 
  TableauDeProcedures<i>; 
end; 
  
//Événement OnCreate de la fiche 
procedure TForm1.FormCreate(Sender: TObject); 
begin 
  Randomize; 
  TableauDeProcedures[0]:=AffichageShowMessage; 
  TableauDeProcedures[1]:=AffichageDansLaFiche; 
  TableauDeProcedures[2]:=AffichageBarreDeTitre; 
end; 
  
end.
Précision sur les variables procédurales : pointeurs ou variables ?

C'est un grand débat qui dépasse le cadre et l'objectif de cette simple QR. En effet, il est tout de même étonnant de constater que bien que ce type se gère comme une variable classique, c'est un type de variable à laquelle s'applique le @, le nil et le Assigned() des pointeurs. Si vous souhaitez approfondir le sujet, nous vous suggérons la lecture de la documentation de Delphi aux rubriques suivantes :
  • Assigned,fonction
  • Types procédure
  • Types procédure dans les instructions et les expressions

Mis à jour le 20 janvier 2014 LadyWasky

Quand vous importez une fonction en langage C qui prend un nombre variable de paramètres, utilisez la directive varargs.

Par exemple :

Code delphi : Sélectionner tout
function printf(Format: PChar): Integer; cdecl; varargs;
La directive varargs fonctionne uniquement avec des routines externes et uniquement avec la convention d'appel cdecl.

Vous trouverez des informations complémentaires dans l'aide de Delphi à la rubrique nommée : Déclarations externes

Mis à jour le 20 janvier 2014 LadyWasky

On souhaite parfois connaître le GUID d'une interface Delphi malheureusement il n'existe pas de méthode dans la VCL pour ce faire. Heureusement les RTTI nous offrent une solution au travers de l'appel à la fonction Typeinfo.

Code delphi : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//Programme de type Console 
uses Typinfo; 
  
var DonneeDeType : PTypeData; 
  
begin 
  //Récupére les information de RTTI d'une interface  
 DonneeDeType := GetTypeData(typeinfo(ISWbemObject)); 
  
  //Vérifie si l'interface posséde bien un GUID 
 if ifHasGuid in DonneeDeType^.IntfFlags then 
   Writeln(GUIDToString(DonneeDeType^.Guid)); 
  
 Writeln(DonneeDeType^.IntfUnit); // Nom de l'unité où est déclarée l'interface 
 readln; 
end.
L'information de type existera pour un type donné, ici une interface de WMI, si un appel à TypeInfo est fait pour lui, ou si le type est mentionné dans la section published d'une classe qui est référencéé dans le code du programme.
La seule différence réside dans le fait que l'appel est effectué à la compilation et que l'on doit lui passer un nom de type et non pas un nom d'instance.

Pour une classe l'appel de GetTypeData(TMonObjet.ClassInfo) est identique à GetTypeData(TypeInfo(TMonObjet)).

Mis à jour le 20 janvier 2014 Laurent Dardenne

Introduction aux RTTI sous Delphi, par Laurent Dardenne

Pour vider le buffer du clavier on utilise l'API PeekMessage qui opère sur la file d'attente des messages du thread courant.

Code delphi : Sélectionner tout
1
2
3
4
5
6
7
procedure VideBufferClavier; 
var 
  Msg: TMsg; 
begin 
  while PeekMessage(Msg, 0, WM_KEYFIRST, WM_KEYLAST, 
    PM_REMOVE or PM_NOYIELD) do; 
end;
Le code précédent recherche les messages claviers et les supprime (PM_REMOVE) du premier (WM_KEYFIRST) jusqu'au dernier ( WM_KEYLAST).

Mis à jour le 20 janvier 2014 LadyWasky

Voici une fonction permettant d'obtenir le nombre de bits "allumés" dans le contenu d'une variable de n'importe quel type.

Code delphi : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function card(var UneVariable; size: integer): integer; 
const // table des bits actifs dans un groupe de 4 bits (exemple : 9=8+1-->2 bits) 
  NBits: array[0..15] of integer=( 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4); 
var 
  t: TbyteArray absolute UneVariable;  //table pointant sur le premier octet de la variable 
                                        //dont on souhaite connaître le nombre de 
                                        //bits allumés 
  j: integer; 
  PoidsFort:byte; 
  PoidsFaible:byte; 
begin 
  result:=0; 
  //Pour chaque octet du tableau 
  for j := 0 to size - 1 do 
  begin 
    //chaque octet est coupé en deux groupe de 4 bits 
    PoidsFort:=t[j] shr 4 
    PoidsFaible:=t[j] and $F; 
    //On dénombre pour chaque groupe de 4 bits le nombre de bits allumés 
    inc(result, NBits[PoidsFort] and $F] + NBits[PoidsFaible]); 
  end; 
end;
L'exemple d'utilisation le plus évident est de connaître la cardinalité d'un ensemble :
Code delphi : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
var MonEnsemble:Set of Byte; 
     NomBreDElements:integer; 
begin 
... 
  MonEnsemble:=[128,25,96,36,200]; 
  
  //on passse à notre fonction notre variable  
  //et aussi sa taille en octets, obtenue grace à la fonction SizeOf() ) 
  NomBreDElements:=Card(MonEnsemble,SizeOf(MonEnsemble)); 
  
  ShowMessage(IntToStr(NomBreDElements)); //affiche "5" 
... 
end;
Mais n'importe qu'elle variable peut faire l'affaire (ce qui n'a que peu d'intérêt si ce n'est pour un entier) :
Code delphi : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var  UnEntier:integer; 
      UnBoolean:Boolean; 
      UnReel:double; 
      UneClasse:TForm; 
      UneChaine:string; 
      UnTableau:array[] of ... //QuelqueChose  
begin 
    ... 
   NomBreDElements:=Card(UnEntier,SizeOf(UnEntier)); 
   NomBreDElements:=Card(UnBoolean,SizeOf(UnBoolean)); 
   NomBreDElements:=Card(UnReel,SizeOf(UnReel)); 
   NomBreDElements:=Card(UneClasse,SizeOf(UneClasse)); 
   NomBreDElements:=Card(UneChaine,SizeOf(UneChaine));     
   NomBreDElements:=Card(UnTableau:,SizeOf(UnTableau));  
   ... 
end;
Exemple concret d'utilisation :
Cet outil mathématique peut être intéressant pour l'élaboration d'un jeu de cartes (par exemple), qui permet de déterminer facilement le nombre de cartes restantes dans la main d'un joueur, la pioche, etc.

À propos de la directive absolute :
cette directive est un relicat du Turbo Pascal, elle permet de créer une nouvelle variable placée à la même adresse qu'une variable existante. Pour cela, il s'agit de placer le mot absolute après le nom de type, dans la déclaration de la nouvelle variable, en le faisant suivre du nom d'une variable existante (déclarée précédemment).
On peut voir cette directive comme un transtypage de variable.

Mis à jour le 20 janvier 2014 LadyWasky

On peut souhaiter quelquefois qu'un traitement au sein d'une même fonction puissent renvoyer un type de donnée différents selon les cas.
Vous trouverez une solution dans le tutoriel cité.

Mis à jour le 20 janvier 2014 LadyWasky Laurent Dardenne

Quelques fois, on peut être amené à manipuler des DLL écrites en C ou C++ utilisant un nombre variable de paramètres. Dans ce cas, la solution se trouve ici.

En revanche, réaliser en natif une méthode utilisant un nombre variable de paramètres nécessite une autre approche. Dans un premier temps, on pourrait penser qu'un tableau de pointeurs suffirait mais au moins 2 questions se posent, à savoir comment :

  • connaître le nombre de paramètres dans un tableau de pointeurs ?
  • retrouver le type de chaque paramètre ?

Les traitements à mettre en place seraient assez délicats. Le langage Delphi nous proposant le type Variant, pouvant encapsuler n'importe quel type Delphi, voyons comment procéder sans trop d'efforts dans le tutoriel cité ci-dessous.

Mis à jour le 20 janvier 2014 LadyWasky Laurent Dardenne

Il existe dans Delphi un raccourci pour créer un GUID lors de l'écriture du code : Ctrl+Shift+G…
Mais comment faire lorsque l'on veut créer ce GUID dynamiquement pendant l'exécution d'un programme ?
Il suffit d'utiliser la fonction CoCreateGuid. On peut donc créer une fonction qui permet de retourner un GUID facilement:

Code delphi : Sélectionner tout
1
2
3
4
5
6
7
8
function CreateGuid: string; 
var 
  GUID: TGUID; 
begin 
  Result := ''; 
  if CoCreateGuid(GUID) = S_OK then 
    Result := GUIDToString(GUID); 
end;
Ne pas oublier de rajouter les unités comObj et ActiveX dans la clause uses.

Note :
À partir de BDS 2006 l'unité comObj propose une fonction identique :
Code delphi : Sélectionner tout
function CreateClassID: string;
et l'unité Sysutils propose la fonction suivante renvoyant un nouveau TGUID :
Code delphi : Sélectionner tout
function CreateGUID(out Guid: TGUID): HResult;

Mis à jour le 20 janvier 2014 Pascal Jankowski

Un If immédiat permet de réaliser une affectation conditionnelle. Il s'agit suivant la valeur de vérité d'une condition (vraie ou fausse) d'affecter une valeur à une variable donnée. Ceci peut se faire soit à travers une construction if...then...else (trois instructions) ou avec l'opérateur ?..: pour ceux qui connaissent le langage C.

A partir de Delphi 6, nous pouvons utiliser l'instruction IfThen dont une signature est :

Code delphi : Sélectionner tout
function IfThen (AValue : Boolean; const ATrue : Integer; const AFalse : Integer = 0) : Integer; overload;
surchargée respectivement pour les types Int64, Double et String et qui renvoie, selon la condition, une des deux valeurs spécifiées. Consultez l'aide de Delphi pour avoir plus de détails.

Pour les versions antérieures à la version 6, le code suivant permet d'implémenter un équivalent de IfThen :
Code delphi : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
Interface 
  
{ Immediate If } 
{ IIF renvoyant un entier } 
function IIF(ACondition: boolean; ATruePart, AFalsePart: integer): integer; overload; 
{ IIF renvoyant un extended } 
function IIF(ACondition: boolean; ATruePart, AFalsePart: Extended): Extended; overload; 
{ IIF renvoyant une string } 
function IIF(ACondition: boolean; ATruePart, AFalsePart: string): string; overload; 
{ IIF renvoyant un objet } 
function IIF(ACondition: boolean; ATruePart, AFalsePart: TObject): TObject; overload; 
  
Implementation 
function IIF(ACondition: boolean; ATruePart, AFalsePart: integer): integer; 
{ Immediate IF pour les entiers } 
begin 
  if ACondition then 
    Result := ATruePart else 
      Result := AFalsePart; 
end; 
  
function IIF(ACondition: boolean; ATruePart, AFalsePart: Extended): Extended; 
{ Immediate IF pour les flottant } 
begin 
  if ACondition then 
    Result := ATruePart else 
      Result := AFalsePart; 
end; 
  
function IIF(ACondition: boolean; ATruePart, AFalsePart: string): string; 
{ Immediate IF pour les chaînes } 
begin 
  if ACondition then 
    Result := ATruePart else 
      Result := AFalsePart; 
end;
Code delphi : Sélectionner tout
1
2
3
4
5
6
7
function IIF(ACondition: boolean; ATruePart, AFalsePart: TObject): TObject; 
{ Immediate IF pour les objets } 
begin 
  if ACondition then 
    Result := ATruePart else 
      Result := AFalsePart; 
end;

Utilisation des fonctions :
Code delphi : Sélectionner tout
1
2
3
4
5
6
7
8
procedure TMyObject.TestIIF; 
var 
  idx: integer; 
  MyString: String; 
begin 
  idx := 10; { Par exemple } 
  MyString := IIF(idx=0,'Zero','Différent de Zero'); 
end;
est équivalent à
Code delphi : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
procedure TMyObject.TestIIF; 
var 
  idx: integer; 
  MyString: String; 
begin 
  idx := 10; { Par exemple } 
  if idx=0 then 
    MyString = 'Zero' else 
      MyString = 'Différent de Zéro'; 
end;
Cela permet de gérer les conditions simples d'affectation de variables.

Mis à jour le 17 octobre 2013 ptitbob

Il est possible avec Delphi de calculer la distance entre deux points GPS, c'est-à-dire la distance à vol d'oiseau. Pour cela, nous allons travailler avec des degrés décimaux (DD). Si vous travaillez en DMS (degrés, minutes, secondes), il faudra convertir au préalable les données.

Il s'agit d'un calcul complexe, car il faut tenir compte de la rotondité de la terre. Pour cela, il faut utiliser une formule de trigonométrie sphérique. Les degrés passent en radians avec DegToRad de l'unité Math. On applique la formule et on passe la valeur qui est en miles puis en kilomètres (1 mile = 1.609344 km).

Code delphi : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
procedure TForm2.Button1Click(Sender: TObject); 
var 
   eLatitude1, eLatitude2   : Extended; 
   eLongitude1, eLongitude2 : Extended; 
   rLatitude1, rLatitude2   : Extended; 
   rTheta                   : Extended; 
   eDistance                : Extended; 
begin 
   // coordonnées GPS de la ville de Dijon (21000) 
   eLatitude1  := 47.366667; 
   eLongitude1 := 5.033333; 
  
   // coordonnées GPS de la ville d'Aubagne (13400) 
   eLatitude2  := 43.283333; 
   eLongitude2 := 5.566667; 
  
   // conversion des degrés en radians 
   rLatitude1  := DegToRad(eLatitude1); 
   rLatitude2  := DegToRad(eLatitude2); 
  
   // différence entre les deux longitudes 
   rTheta      := DegToRad(eLongitude1 - eLongitude2); 
  
   // trigonométrie circulaire 
   eDistance   := Sin(rLatitude1) * Sin(rLatitude2) + Cos(rLatitude1) * Cos(rLatitude2) * Cos(rTheta); 
   eDistance   := ArcCos(eDistance); 
   eDistance   := eDistance * 180 / Pi; 
   // 60 minutes par degré, et passage de mile en mile nautique 
   eDistance   := eDistance * 60 * 1.1515; 
  
   // passage des miles en kilomètres 
   eDistance   := eDistance * 1.609344; 
  
   // affichage de la valeur 
   ShowMessage(eDistance.ToString); 
end;

Mis à jour le 22 novembre 2016

Dans certaines situations, on peut vouloir adapter certains composants visuels pour refléter leur état.
Par exemple, on peut vouloir désactiver ou activer des composants, changer leur couleur de fond, vider leur contenu, etc.

Pour modifier une ou plusieurs propriétés d'un ensemble de composants visuels, on peut le faire individuellement pour chacun.
Mais on peut utiliser aussi le RTTI.
Voilà une petite classe sans prétention qui permet ce genre de chose :

Code Delphi : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
unit UStateManager; 
  
interface 
  
uses System.SysUtils, System.Classes, System.StrUtils, System.Rtti, 
     Winapi.Windows, Vcl.Controls; 
  
type 
  
  TStateManager = class(TObject) 
  public 
    /// <summary> 
    ///   Permet de définir à l'avance le Owner sur lequel sera applliqué la traitement 
    /// </summary> 
    class var TheOwner: TWinControl; 
    /// <summary> 
    ///  Permet de modifier plusieurs TControls sur un objet visuel qui en contient plusieurs 
    /// </summary> 
    /// <param name="OwnerCtrls"> 
    ///   Composant qui contient les éléments à traiter 
    /// </param> 
    /// <param name="PropNames"> 
    ///  Les noms des propriétés à traiter 
    /// </param> 
    /// <param name="Values"> 
    ///   Les valeurs pour chaque propriété de PropNames 
    /// </param> 
    class procedure ChangeState(OwnerCtrls: TWinControl; PropNames: array of string; Values: array of const); 
  
    class procedure EnableCtrls(OwnerCtrls: TWinControl; State: Boolean; EnabledOrReaddOnly: Boolean = False); 
  end; 
  
implementation 
  
{ TStateManager } 
  
procedure AssignValue(Control: TControl; Prop: TRttiProperty; Valeur: TVarRec); 
begin 
  case Valeur.VType of 
    vtInteger: 
      Prop.SetValue(Control, Valeur.VInteger); 
    vtBoolean: 
      Prop.SetValue(Control, Valeur.VBoolean); 
    vtChar: 
      Prop.SetValue(Control, Valeur.VChar); 
    vtExtended: 
      Prop.SetValue(Control, Valeur.VExtended^); 
    vtString: 
      Prop.SetValue(Control, Valeur.VString^); 
    vtWideChar: 
      Prop.SetValue(Control, Valeur.VWideChar); 
    vtAnsiString: 
      Prop.SetValue(Control, AnsiString(Valeur.VAnsiString^)); 
    vtWideString: 
      Prop.SetValue(Control, WideString(Valeur.VWideString^)); 
    vtInt64: 
      Prop.SetValue(Control, Int64(Valeur.VInt64)); 
    vtUnicodeString: 
      Prop.SetValue(Control, UnicodeString(Valeur.VUnicodeString)); 
  end; 
end; 
  
class procedure TStateManager.ChangeState(OwnerCtrls: TWinControl; 
  PropNames: array of string; Values: array of const); 
var 
  I, J: Integer; 
  Ctx : TRttiContext; 
  Typ : TRttiType; 
  Prop: TRttiProperty; 
  P   : Pointer; 
begin 
  if not Assigned(OwnerCtrls) then 
    if Assigned(TStateManager.TheOwner) then 
      OwnerCtrls := TStateManager.TheOwner 
    else 
      raise Exception.Create('No defined Owner'); 
  
  for I := 0 to Pred(OwnerCtrls.ControlCount) do 
  begin 
    P := OwnerCtrls.Controls[I].ClassInfo; 
    if Assigned(P) then 
    begin 
      Typ := Ctx.GetType(P); 
      for J := Low(PropNames) to High(PropNames) do 
      begin 
        Prop := Typ.GetProperty(PropNames[J]); 
        if Assigned(Prop) then 
          AssignValue(OwnerCtrls.Controls[I], Prop, Values[J]); 
      end; 
    end; 
  end; 
end; 
  
class procedure TStateManager.EnableCtrls(OwnerCtrls: TWinControl; 
  State: Boolean; EnabledOrReaddOnly: Boolean); 
var 
  I   : Integer; 
  Ctx : TRttiContext; 
  Typ : TRttiType; 
  Prop: TRttiProperty; 
  P   : Pointer; 
  Vl  : TVarRec; 
begin 
  
  if not Assigned(OwnerCtrls) then 
    if Assigned(TStateManager.TheOwner) then 
      OwnerCtrls := TStateManager.TheOwner 
    else 
      raise Exception.Create('No defined Owner'); 
  
  Vl.VType := vtBoolean; 
  Vl.VBoolean := State; 
  
  for I := 0 to Pred(OwnerCtrls.ControlCount) do 
  begin 
    P := OwnerCtrls.Controls[I].ClassInfo; 
  
    if Assigned(P) then 
    begin 
      Typ := Ctx.GetType(P); 
  
      Prop := Typ.GetProperty('Enabled'); 
      if Assigned(Prop) then 
        AssignValue(OwnerCtrls.Controls[I], Prop, Vl) 
      else 
      begin 
        if EnabledOrReaddOnly then 
        begin 
          Prop := Typ.GetProperty('ReadOnly'); 
          if Assigned(Prop) then 
            AssignValue(OwnerCtrls.Controls[I], Prop, Vl) 
        end; 
      end; 
  
    end; 
  
    Prop := nil; 
  end; 
end; 
  
end.

Et un exemple d'utilisation :

Code Delphi : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
unit Unit5; 
  
interface 
  
uses 
  Winapi.Windows, Winapi.Messages, 
  System.SysUtils, System.Variants, System.Classes, System.Rtti, System.TypInfo, 
  Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.Mask, 
  Vcl.Wwdbedit, Vcl.ExtCtrls, Vcl.Wwdotdot, Vcl.Wwdbcomb; 
  
type 
  TForm5 = class(TForm) 
    Edit1: TEdit; 
    Label1: TLabel; 
    Button1: TButton; 
    RadioButton1: TRadioButton; 
    Panel1: TPanel; 
    wwDBEdit1: TwwDBEdit; 
    Memo1: TMemo; 
    wwDBComboBox1: TwwDBComboBox; 
    Panel2: TPanel; 
    Memo2: TMemo; 
    procedure FormDblClick(Sender: TObject); 
  end; 
  
var 
  Form5: TForm5; 
  
implementation 
  
{$R *.dfm} 
  
uses UStateManager; 
  
procedure TForm5.FormDblClick(Sender: TObject); 
begin 
  
  TStateManager.TheOwner := Panel2; 
  TStateManager.ChangeState(nil, ['color', 'readonly', 'Text'], [clYellow, False, 'Hello']); 
  // or ... 
  TStateManager.ChangeState(Panel2, ['color', 'readonly', 'Text'], [clYellow, False, 'Hello']); 
  
  TStateManager.EnableCtrls(Panel2, False, True); 
end; 
  
end.

Mis à jour le 31 mai 2024 Papy214

Proposer une nouvelle réponse sur la FAQ

Ce n'est pas l'endroit pour poser des questions, allez plutôt sur le forum de la rubrique pour ça


Réponse à la question

Liens sous la question
précédent sommaire suivant
 

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 © 2024 Developpez Developpez LLC. Tous droits réservés Developpez LLC. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents et images sans l'autorisation expresse de Developpez LLC. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.