Les Identifiants Uniques Universels (UUIDs), également connus sous le nom de GUIDs (Globally Unique Identifiers) dans le développement Windows, sont des valeurs de 128 bits qui fournissent une identification unique à travers les systèmes distribués. Dans la programmation Delphi, les GUIDs sont essentiels pour les interfaces COM, les clés primaires de base de données, les applications distribuées et pour garantir l'unicité sans coordination centralisée. Que vous développiez des applications de bureau avec VCL/FMX ou des applications serveur, comprendre la génération d'UUID est crucial.
Ce guide complet explore plusieurs approches pour générer des UUIDs dans les applications Delphi, couvrant à la fois les fonctions RTL natives et les appels API Windows. Nous examinerons les stratégies d'implémentation pour différentes versions de Delphi, du classique Delphi 7 aux dernières versions de RAD Studio, ainsi que les considérations multiplateformes pour les applications FireMonkey. Les techniques présentées ici s'appliquent également au développement Free Pascal et Lazarus.
La façon la plus simple et la plus courante de générer un UUID en Delphi est d'utiliser la fonction intégrée CreateGUID de l'unité System.SysUtils. Cette méthode fonctionne sur toutes les versions et plateformes Delphi.
program GenererUUID;
{$APPTYPE CONSOLE}
uses
System.SysUtils;
var
MonGUID: TGUID;
ChaineGUID: string;
begin
try
// Générer un nouveau GUID
if CreateGUID(MonGUID) = S_OK then
begin
// Convertir le GUID en représentation chaîne
ChaineGUID := GUIDToString(MonGUID);
WriteLn('UUID Généré: ', ChaineGUID);
// Exemple de sortie: UUID Généré: {8C2D56F0-8A1E-4B6C-A8D2-AF3A99B11F75}
// Alternative: Obtenir la chaîne sans accolades
ChaineGUID := MonGUID.ToString;
WriteLn('Sans accolades: ', ChaineGUID);
// Exemple de sortie: Sans accolades: 8C2D56F0-8A1E-4B6C-A8D2-AF3A99B11F75
end
else
WriteLn('Échec de création du GUID');
ReadLn;
except
on E: Exception do
WriteLn('Erreur: ', E.Message);
end;
end.
CreateGUID génère un UUID Version 4 (aléatoire) en utilisant le générateur de nombres aléatoires cryptographiquement sûr du système d'exploitation. Sur Windows, il appelle CoCreateGuid en interne, tandis que sur d'autres plateformes, il utilise les fonctions système appropriées. La fonction retourne S_OK (0) en cas de succès. Le type d'enregistrement TGUID représente la structure GUID, et GUIDToString la convertit au format de chaîne standard avec accolades.
Pour les applications spécifiques à Windows ou lorsque vous avez besoin de plus de contrôle, vous pouvez appeler directement la fonction CoCreateGuid de l'API Windows. Cette approche nécessite l'unité Winapi.ActiveX.
program GUIDAPIWindows;
{$APPTYPE CONSOLE}
uses
System.SysUtils,
Winapi.Windows,
Winapi.ActiveX;
function GenererGUIDWindows: string;
var
NouveauGUID: TGUID;
hr: HResult;
begin
// Initialiser COM (requis pour CoCreateGuid)
CoInitialize(nil);
try
// Appeler directement l'API Windows
hr := CoCreateGuid(NouveauGUID);
if Succeeded(hr) then
begin
// Convertir au format chaîne
Result := GUIDToString(NouveauGUID);
end
else
begin
raise Exception.CreateFmt('CoCreateGuid a échoué avec l''erreur: %d', [hr]);
end;
finally
// Nettoyer COM
CoUninitialize;
end;
end;
begin
try
WriteLn('GUID API Windows: ', GenererGUIDWindows);
// Générer plusieurs GUIDs
WriteLn('Génération de 5 GUIDs:');
for var i := 1 to 5 do
WriteLn(Format(' GUID %d: %s', [i, GenererGUIDWindows]));
ReadLn;
except
on E: Exception do
WriteLn('Erreur: ', E.Message);
end;
end.
Cette méthode interface directement avec le sous-système COM de Windows pour générer des GUIDs. CoCreateGuid est l'API Windows sous-jacente que CreateGUID appelle sur les plateformes Windows. Bien que cette approche nécessite l'initialisation COM, elle fournit un accès direct à la génération de GUID Windows et peut être utile lors du travail avec des interfaces COM ou lorsque vous devez vous assurer que vous utilisez l'implémentation spécifique à Windows.
Delphi permet un formatage flexible des GUIDs pour répondre à diverses exigences, comme supprimer les accolades, utiliser différents séparateurs ou créer des représentations personnalisées pour des systèmes spécifiques.
program GUIDsFormates;
{$APPTYPE CONSOLE}
uses
System.SysUtils;
type
TGUIDHelper = record helper for TGUID
function VersChainCompacte: string;
function VersChaineMajuscules: string;
function VersFormatRegistre: string;
function VersFormatURN: string;
end;
function TGUIDHelper.VersChainCompacte: string;
begin
// Supprimer tout le formatage (32 chiffres hex)
Result := Format('%.8x%.4x%.4x%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x',
[D1, D2, D3, D4[0], D4[1], D4[2], D4[3], D4[4], D4[5], D4[6], D4[7]]);
end;
function TGUIDHelper.VersChaineMajuscules: string;
begin
Result := UpperCase(ToString);
end;
function TGUIDHelper.VersFormatRegistre: string;
begin
// Le format registre utilise des accolades
Result := GUIDToString(Self);
end;
function TGUIDHelper.VersFormatURN: string;
begin
// Format URN: urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
Result := 'urn:uuid:' + ToString;
end;
var
MonGUID: TGUID;
begin
CreateGUID(MonGUID);
WriteLn('Format standard: ', MonGUID.ToString);
WriteLn('Avec accolades: ', GUIDToString(MonGUID));
WriteLn('Compact (sans tirets):', MonGUID.VersChainCompacte);
WriteLn('Majuscules: ', MonGUID.VersChaineMajuscules);
WriteLn('Format registre: ', MonGUID.VersFormatRegistre);
WriteLn('Format URN: ', MonGUID.VersFormatURN);
// Formatage personnalisé avec séparateurs spécifiques
WriteLn('Format personnalisé: ',
Format('%.8x_%.4x_%.4x_%.2x%.2x_%.2x%.2x%.2x%.2x%.2x%.2x',
[MonGUID.D1, MonGUID.D2, MonGUID.D3, MonGUID.D4[0], MonGUID.D4[1],
MonGUID.D4[2], MonGUID.D4[3], MonGUID.D4[4], MonGUID.D4[5],
MonGUID.D4[6], MonGUID.D4[7]]));
ReadLn;
end.
Cet exemple démontre diverses options de formatage GUID en utilisant un helper d'enregistrement. L'enregistrement TGUID contient les champs D1 (longword), D2 et D3 (word), et D4 (tableau de 8 octets) qui représentent la structure GUID. Vous pouvez accéder directement à ces champs pour un formatage personnalisé. Le format compact est utile pour les URLs ou lorsque l'espace de stockage est limité, tandis que le format URN est utilisé dans divers standards Internet.
Bien que les GUIDs aléatoires conviennent à la plupart des cas, vous avez parfois besoin d'UUIDs déterministes qui peuvent être régénérés à partir d'entrées spécifiques. Cette implémentation crée des UUIDs Version 5 en utilisant le hachage SHA-1.
program GUIDDeterministe;
{$APPTYPE CONSOLE}
uses
System.SysUtils,
System.Hash;
type
// GUIDs d'espace de noms standard de RFC 4122
TEspaceNomsGUID = record
const DNS: TGUID = '{6ba7b810-9dad-11d1-80b4-00c04fd430c8}';
const URL: TGUID = '{6ba7b811-9dad-11d1-80b4-00c04fd430c8}';
const OID: TGUID = '{6ba7b812-9dad-11d1-80b4-00c04fd430c8}';
const X500: TGUID = '{6ba7b814-9dad-11d1-80b4-00c04fd430c8}';
end;
function CreerGUIDBaseNom(const EspaceNomsGUID: TGUID; const Nom: string): TGUID;
var
Hash: THashSHA1;
OctetsHash: TBytes;
OctetsEspaceNoms: TBytes;
OctetsNom: TBytes;
OctetsCombines: TBytes;
i: Integer;
begin
// Convertir le GUID d'espace de noms en octets
SetLength(OctetsEspaceNoms, 16);
Move(EspaceNomsGUID.D1, OctetsEspaceNoms[0], 4);
Move(EspaceNomsGUID.D2, OctetsEspaceNoms[4], 2);
Move(EspaceNomsGUID.D3, OctetsEspaceNoms[6], 2);
Move(EspaceNomsGUID.D4[0], OctetsEspaceNoms[8], 8);
// Échanger l'ordre des octets pour l'ordre réseau (big-endian)
// D1 est 4 octets
for i := 0 to 1 do
begin
var temp := OctetsEspaceNoms[i];
OctetsEspaceNoms[i] := OctetsEspaceNoms[3-i];
OctetsEspaceNoms[3-i] := temp;
end;
// D2 est 2 octets
var temp := OctetsEspaceNoms[4];
OctetsEspaceNoms[4] := OctetsEspaceNoms[5];
OctetsEspaceNoms[5] := temp;
// D3 est 2 octets
temp := OctetsEspaceNoms[6];
OctetsEspaceNoms[6] := OctetsEspaceNoms[7];
OctetsEspaceNoms[7] := temp;
// Convertir le nom en octets UTF-8
OctetsNom := TEncoding.UTF8.GetBytes(Nom);
// Combiner l'espace de noms et le nom
SetLength(OctetsCombines, Length(OctetsEspaceNoms) + Length(OctetsNom));
Move(OctetsEspaceNoms[0], OctetsCombines[0], Length(OctetsEspaceNoms));
Move(OctetsNom[0], OctetsCombines[Length(OctetsEspaceNoms)], Length(OctetsNom));
// Calculer le hash SHA-1
Hash := THashSHA1.Create;
Hash.Update(OctetsCombines);
OctetsHash := Hash.HashAsBytes;
// Copier les 16 premiers octets vers le GUID
Move(OctetsHash[0], Result.D1, 4);
Move(OctetsHash[4], Result.D2, 2);
Move(OctetsHash[6], Result.D3, 2);
Move(OctetsHash[8], Result.D4[0], 8);
// Définir les bits de version (5) et de variante
Result.D3 := (Result.D3 and $0FFF) or $5000; // Version 5
Result.D4[0] := (Result.D4[0] and $3F) or $80; // Variante 10
end;
begin
try
var NomTest := 'exemple.com/utilisateur/12345';
// Générer des GUIDs déterministes
var GUID1 := CreerGUIDBaseNom(TEspaceNomsGUID.URL, NomTest);
WriteLn('GUID basé sur URL pour "', NomTest, '":');
WriteLn(' ', GUIDToString(GUID1));
// La même entrée produit la même sortie
var GUID2 := CreerGUIDBaseNom(TEspaceNomsGUID.URL, NomTest);
WriteLn(' Même entrée: ', GUIDToString(GUID2));
WriteLn(' Égal: ', GUIDToString(GUID1) = GUIDToString(GUID2));
WriteLn;
// Différents espaces de noms produisent différents GUIDs
var GUID3 := CreerGUIDBaseNom(TEspaceNomsGUID.DNS, 'exemple.com');
WriteLn('GUID basé sur DNS pour "exemple.com":');
WriteLn(' ', GUIDToString(GUID3));
ReadLn;
except
on E: Exception do
WriteLn('Erreur: ', E.Message);
end;
end.
Cette implémentation crée des UUIDs RFC 4122 Version 5 en utilisant le hachage SHA-1. La même combinaison d'espace de noms et de nom produit toujours le même UUID, ce qui le rend idéal pour les systèmes distribués où différents nœuds doivent générer des identifiants identiques pour la même ressource. L'espace de noms fournit le contexte (DNS, URL, etc.) tandis que le nom est l'identifiant spécifique. Notez que la conversion de l'ordre des octets est nécessaire car le RFC spécifie l'ordre réseau.
Pour les applications qui utilisent fréquemment des UUIDs, il est bénéfique de créer une unité utilitaire qui centralise la logique de génération d'UUID. Voici une unité utilitaire complète pour les applications Delphi :
unit UtilitairesUUID;
interface
uses
System.SysUtils, System.Hash;
type
/// <summary>
/// Classe utilitaire pour la génération et manipulation UUID/GUID
/// </summary>
TGenerateurUUID = class
public
type
TVersionUUID = (uvAleatoire, uvBaseeTemps, uvBaseeNom);
class var
// Espaces de noms standard de RFC 4122
EspaceNomsDNS: TGUID;
EspaceNomsURL: TGUID;
EspaceNomsOID: TGUID;
EspaceNomsX500: TGUID;
private
class constructor Create;
class function EchangerEndian(const AGUID: TGUID): TGUID;
public
/// <summary>Générer un UUID aléatoire (Version 4)</summary>
class function NouveauGUID: TGUID;
/// <summary>Générer un UUID séquentiel pour optimisation base de données</summary>
class function NouveauGUIDSequentiel: TGUID;
/// <summary>Générer un UUID basé sur un nom (Version 5)</summary>
class function NouveauGUIDBaseNom(const EspaceNomsGUID: TGUID;
const Nom: string): TGUID;
/// <summary>Générer UUID déterministe basé sur URL</summary>
class function NouveauGUIDBaseURL(const URL: string): TGUID;
/// <summary>Générer UUID déterministe basé sur DNS</summary>
class function NouveauGUIDBaseDNS(const DNS: string): TGUID;
/// <summary>Parser chaîne vers GUID avec validation</summary>
class function EssayerParserGUID(const S: string; out GUID: TGUID): Boolean;
/// <summary>Vérifier si GUID est vide/nil</summary>
class function EstGUIDVide(const GUID: TGUID): Boolean;
/// <summary>Obtenir GUID sans accolades ni tirets</summary>
class function ObtenirGUIDCompact(const GUID: TGUID): string;
end;
implementation
uses
System.DateUtils, Winapi.Windows;
{ TGenerateurUUID }
class constructor TGenerateurUUID.Create;
begin
// Initialiser les espaces de noms standard
EspaceNomsDNS := StringToGUID('{6ba7b810-9dad-11d1-80b4-00c04fd430c8}');
EspaceNomsURL := StringToGUID('{6ba7b811-9dad-11d1-80b4-00c04fd430c8}');
EspaceNomsOID := StringToGUID('{6ba7b812-9dad-11d1-80b4-00c04fd430c8}');
EspaceNomsX500 := StringToGUID('{6ba7b814-9dad-11d1-80b4-00c04fd430c8}');
end;
class function TGenerateurUUID.NouveauGUID: TGUID;
begin
if CreateGUID(Result) <> S_OK then
raise Exception.Create('Échec de création du GUID');
end;
class function TGenerateurUUID.NouveauGUIDSequentiel: TGUID;
var
Horodatage: Int64;
Compteur: Word;
begin
// Générer GUID de base
Result := NouveauGUID;
// Obtenir l'horodatage actuel
Horodatage := DateTimeToUnix(Now, False) * 10000000;
// Intégrer l'horodatage dans les 6 premiers octets pour ordre séquentiel
Result.D1 := LongWord(Horodatage shr 16);
Result.D2 := Word(Horodatage);
// Conserver les bits de version 4 et de variante
Result.D3 := (Result.D3 and $0FFF) or $4000;
Result.D4[0] := (Result.D4[0] and $3F) or $80;
end;
class function TGenerateurUUID.EchangerEndian(const AGUID: TGUID): TGUID;
begin
Result := AGUID;
// Échanger D1 (4 octets)
Result.D1 := ((AGUID.D1 and $000000FF) shl 24) or
((AGUID.D1 and $0000FF00) shl 8) or
((AGUID.D1 and $00FF0000) shr 8) or
((AGUID.D1 and $FF000000) shr 24);
// Échanger D2 (2 octets)
Result.D2 := ((AGUID.D2 and $00FF) shl 8) or
((AGUID.D2 and $FF00) shr 8);
// Échanger D3 (2 octets)
Result.D3 := ((AGUID.D3 and $00FF) shl 8) or
((AGUID.D3 and $FF00) shr 8);
end;
class function TGenerateurUUID.NouveauGUIDBaseNom(const EspaceNomsGUID: TGUID;
const Nom: string): TGUID;
var
Hash: THashSHA1;
OctetsHash: TBytes;
OctetsEspaceNoms: TBytes;
GUIDEchange: TGUID;
begin
// Convertir en ordre réseau
GUIDEchange := EchangerEndian(EspaceNomsGUID);
// Préparer les données pour le hachage
SetLength(OctetsEspaceNoms, 16);
Move(GUIDEchange, OctetsEspaceNoms[0], 16);
// Calculer le hash
Hash := THashSHA1.Create;
Hash.Update(OctetsEspaceNoms);
Hash.Update(TEncoding.UTF8.GetBytes(Nom));
OctetsHash := Hash.HashAsBytes;
// Créer GUID à partir du hash
Move(OctetsHash[0], Result, 16);
// Reconvertir depuis l'ordre réseau
Result := EchangerEndian(Result);
// Définir version 5 et variante
Result.D3 := (Result.D3 and $0FFF) or $5000;
Result.D4[0] := (Result.D4[0] and $3F) or $80;
end;
class function TGenerateurUUID.NouveauGUIDBaseURL(const URL: string): TGUID;
begin
Result := NouveauGUIDBaseNom(EspaceNomsURL, URL);
end;
class function TGenerateurUUID.NouveauGUIDBaseDNS(const DNS: string): TGUID;
begin
Result := NouveauGUIDBaseNom(EspaceNomsDNS, DNS);
end;
class function TGenerateurUUID.EssayerParserGUID(const S: string;
out GUID: TGUID): Boolean;
begin
try
GUID := StringToGUID(S);
Result := True;
except
Result := False;
end;
end;
class function TGenerateurUUID.EstGUIDVide(const GUID: TGUID): Boolean;
const
GUIDVide: TGUID = '{00000000-0000-0000-0000-000000000000}';
begin
Result := CompareMem(@GUID, @GUIDVide, SizeOf(TGUID));
end;
class function TGenerateurUUID.ObtenirGUIDCompact(const GUID: TGUID): string;
begin
Result := Format('%.8x%.4x%.4x%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x',
[GUID.D1, GUID.D2, GUID.D3, GUID.D4[0], GUID.D4[1], GUID.D4[2],
GUID.D4[3], GUID.D4[4], GUID.D4[5], GUID.D4[6], GUID.D4[7]]);
end;
end.
Cette unité utilitaire fournit un ensemble complet de méthodes de génération d'UUID encapsulées dans une classe avec des méthodes de classe. Elle inclut la génération d'UUID aléatoire, les UUIDs séquentiels pour l'optimisation de base de données, et les UUIDs déterministes basés sur des noms suivant RFC 4122. L'unité gère la conversion d'endianness pour une génération correcte d'UUID Version 5 et fournit des méthodes d'aide pour l'analyse et le formatage. Vous pouvez facilement étendre cette unité avec des fonctionnalités supplémentaires comme la génération d'UUID Version 1 (basée sur le temps) ou Version 3 (basée sur MD5).
Lors du travail avec des GUIDs provenant de sources externes ou d'entrées utilisateur, une validation appropriée est cruciale. Voici comment analyser et valider les GUIDs dans les applications Delphi :
program ValiderGUIDs;
{$APPTYPE CONSOLE}
uses
System.SysUtils, System.RegularExpressions;
type
TValidateurGUID = class
private
const
// Modèle GUID conforme RFC 4122
ModeleGUID = '^[{(]?[0-9A-Fa-f]{8}[-]?([0-9A-Fa-f]{4}[-]?){3}[0-9A-Fa-f]{12}[)}]?$';
public
class function EstGUIDValide(const Valeur: string): Boolean;
class function EssayerParserGUID(const Valeur: string; out GUID: TGUID): Boolean;
class function NormaliserGUID(const Valeur: string): string;
class function ExtraireGUIDDeTexte(const Texte: string): TArray<string>;
end;
class function TValidateurGUID.EstGUIDValide(const Valeur: string): Boolean;
var
RegEx: TRegEx;
begin
RegEx := TRegEx.Create(ModeleGUID);
Result := RegEx.IsMatch(Valeur);
end;
class function TValidateurGUID.EssayerParserGUID(const Valeur: string;
out GUID: TGUID): Boolean;
var
ValeurNormalisee: string;
begin
Result := False;
// D'abord vérifier avec regex
if not EstGUIDValide(Valeur) then
Exit;
try
// Normaliser la chaîne GUID
ValeurNormalisee := NormaliserGUID(Valeur);
// Essayer de convertir
GUID := StringToGUID(ValeurNormalisee);
Result := True;
except
// Format invalide
Result := False;
end;
end;
class function TValidateurGUID.NormaliserGUID(const Valeur: string): string;
var
ValeurPropre: string;
begin
// Supprimer tous les caractères non-hexadécimaux
ValeurPropre := TRegEx.Replace(Valeur, '[^0-9A-Fa-f]', '');
// Vérifier si nous avons exactement 32 chiffres hexadécimaux
if Length(ValeurPropre) <> 32 then
raise Exception.Create('Longueur GUID invalide');
// Formater comme GUID standard
Result := Format('{%s-%s-%s-%s-%s}',
[Copy(ValeurPropre, 1, 8),
Copy(ValeurPropre, 9, 4),
Copy(ValeurPropre, 13, 4),
Copy(ValeurPropre, 17, 4),
Copy(ValeurPropre, 21, 12)]);
end;
class function TValidateurGUID.ExtraireGUIDDeTexte(const Texte: string): TArray<string>;
var
RegEx: TRegEx;
Correspondances: TMatchCollection;
Correspondance: TMatch;
i: Integer;
begin
RegEx := TRegEx.Create(ModeleGUID);
Correspondances := RegEx.Matches(Texte);
SetLength(Result, Correspondances.Count);
for i := 0 to Correspondances.Count - 1 do
Result[i] := Correspondances[i].Value;
end;
// Tester le validateur
var
ChainesTest: array[0..5] of string = (
'{8C2D56F0-8A1E-4B6C-A8D2-AF3A99B11F75}', // Valide avec accolades
'8C2D56F0-8A1E-4B6C-A8D2-AF3A99B11F75', // Valide sans accolades
'8C2D56F08A1E4B6CA8D2AF3A99B11F75', // Valide sans formatage
'(8C2D56F0-8A1E-4B6C-A8D2-AF3A99B11F75)', // Valide avec parenthèses
'pas-un-guid', // Invalide
'8C2D56F0-8A1E-4B6C-A8D2-AF3A99B11F7' // Invalide (trop court)
);
GUIDTest: TGUID;
ChaineTest: string;
begin
try
WriteLn('Tests de Validation GUID:');
WriteLn('='*50);
for ChaineTest in ChainesTest do
begin
Write(Format('%-40s: ', [ChaineTest]));
if TValidateurGUID.EssayerParserGUID(ChaineTest, GUIDTest) then
WriteLn('Valide - Analysé comme ', GUIDToString(GUIDTest))
else
WriteLn('Invalide');
end;
WriteLn;
WriteLn('Extraction de GUIDs du texte:');
WriteLn('='*50);
var TexteExemple := 'L''ID utilisateur est {8C2D56F0-8A1E-4B6C-A8D2-AF3A99B11F75} ' +
'et l''ID de session est 123e4567-e89b-12d3-a456-426614174000.';
var GUIDsExtraits := TValidateurGUID.ExtraireGUIDDeTexte(TexteExemple);
WriteLn('Texte: ', TexteExemple);
WriteLn('GUIDs trouvés:');
for ChaineTest in GUIDsExtraits do
WriteLn(' - ', ChaineTest);
ReadLn;
except
on E: Exception do
WriteLn('Erreur: ', E.Message);
end;
end.
Cet exemple de validation démontre une validation complète des GUID utilisant des expressions régulières. La classe TValidateurGUID peut valider les GUIDs dans divers formats (avec/sans accolades, tirets ou parenthèses), les normaliser vers un format standard et extraire les GUIDs de blocs de texte plus importants. Le modèle regex correspond à toutes les représentations courantes de GUID tandis que la fonction de normalisation assure un formatage cohérent. C'est particulièrement utile lors du traitement des entrées utilisateur ou des données provenant de systèmes externes.
Lors de la génération de GUIDs à grande échelle dans les applications Delphi, les performances deviennent critiques. Voici comment optimiser la génération de GUID et mesurer les performances :
program PerformanceGUID;
{$APPTYPE CONSOLE}
uses
System.SysUtils, System.Diagnostics, System.Classes,
System.Generics.Collections, System.Threading;
type
TCacheGUID = class
private
FCache: TThreadList<TGUID>;
FTailleCache: Integer;
FSeuilRemplissage: Integer;
procedure RemplirCache;
public
constructor Create(ATailleCache: Integer = 1000);
destructor Destroy; override;
function ObtenirGUID: TGUID;
property TailleCache: Integer read FTailleCache;
end;
constructor TCacheGUID.Create(ATailleCache: Integer);
begin
inherited Create;
FTailleCache := ATailleCache;
FSeuilRemplissage := ATailleCache div 4;
FCache := TThreadList<TGUID>.Create;
RemplirCache;
end;
destructor TCacheGUID.Destroy;
begin
FCache.Free;
inherited;
end;
procedure TCacheGUID.RemplirCache;
var
Liste: TList<TGUID>;
i: Integer;
NouveauGUID: TGUID;
begin
Liste := FCache.LockList;
try
// Remplir le cache à capacité
while Liste.Count < FTailleCache do
begin
CreateGUID(NouveauGUID);
Liste.Add(NouveauGUID);
end;
finally
FCache.UnlockList;
end;
end;
function TCacheGUID.ObtenirGUID: TGUID;
var
Liste: TList<TGUID>;
begin
Liste := FCache.LockList;
try
if Liste.Count = 0 then
RemplirCache;
Result := Liste.Last;
Liste.Delete(Liste.Count - 1);
// Remplir le cache de manière asynchrone si en dessous du seuil
if Liste.Count < FSeuilRemplissage then
begin
TTask.Run(procedure
begin
RemplirCache;
end);
end;
finally
FCache.UnlockList;
end;
end;
// Évaluer différentes approches
procedure EvaluerGenerationGUID(Nombre: Integer);
var
Chronometre: TStopwatch;
GUIDs: TArray<TGUID>;
i: Integer;
Cache: TCacheGUID;
procedure EvaluerStandard;
begin
Chronometre := TStopwatch.StartNew;
SetLength(GUIDs, Nombre);
for i := 0 to Nombre - 1 do
CreateGUID(GUIDs[i]);
Chronometre.Stop;
WriteLn(Format('CreateGUID standard: %d ms pour %d GUIDs (%.2f GUIDs/ms)',
[Chronometre.ElapsedMilliseconds, Nombre,
Nombre / Max(1, Chronometre.ElapsedMilliseconds)]));
end;
procedure EvaluerAvecCache;
begin
Cache := TCacheGUID.Create(1000);
try
Chronometre := TStopwatch.StartNew;
SetLength(GUIDs, Nombre);
for i := 0 to Nombre - 1 do
GUIDs[i] := Cache.ObtenirGUID;
Chronometre.Stop;
WriteLn(Format('Génération avec cache: %d ms pour %d GUIDs (%.2f GUIDs/ms)',
[Chronometre.ElapsedMilliseconds, Nombre,
Nombre / Max(1, Chronometre.ElapsedMilliseconds)]));
finally
Cache.Free;
end;
end;
procedure EvaluerParallele;
begin
Chronometre := TStopwatch.StartNew;
SetLength(GUIDs, Nombre);
TParallel.For(0, Nombre - 1,
procedure(Index: Integer)
begin
CreateGUID(GUIDs[Index]);
end);
Chronometre.Stop;
WriteLn(Format('Génération parallèle: %d ms pour %d GUIDs (%.2f GUIDs/ms)',
[Chronometre.ElapsedMilliseconds, Nombre,
Nombre / Max(1, Chronometre.ElapsedMilliseconds)]));
end;
begin
WriteLn(Format('Évaluation de la génération de %d GUIDs...', [Nombre]));
WriteLn('='*60);
EvaluerStandard;
EvaluerAvecCache;
EvaluerParallele;
WriteLn;
WriteLn('Utilisation mémoire:');
WriteLn(Format(' Un seul GUID: %d octets', [SizeOf(TGUID)]));
WriteLn(Format(' %d GUIDs: %.2f Mo', [Nombre, (Nombre * SizeOf(TGUID)) / (1024 * 1024)]));
end;
begin
try
// Tester avec différentes tailles
EvaluerGenerationGUID(10000);
WriteLn;
EvaluerGenerationGUID(100000);
WriteLn;
EvaluerGenerationGUID(1000000);
ReadLn;
except
on E: Exception do
WriteLn('Erreur: ', E.Message);
end;
end.
Cet exemple d'optimisation des performances démontre trois approches pour la génération de GUID en volume élevé : génération séquentielle standard, génération avec cache avec remplissage en arrière-plan, et génération parallèle utilisant TParallel. L'approche avec cache pré-génère des GUIDs et remplit le cache de manière asynchrone, réduisant la latence pour les applications nécessitant des GUIDs à la demande. La génération parallèle exploite plusieurs cœurs CPU pour les opérations en masse. Les évaluations aident à identifier l'approche la plus adaptée à votre cas d'utilisation spécifique. Notez que CreateGUID est thread-safe, rendant la génération parallèle sûre sans synchronisation supplémentaire.
Lors de l'implémentation de la génération d'UUID dans les applications Delphi, choisissez la méthode appropriée en fonction de vos exigences. Pour les identifiants uniques à usage général, CreateGUID fournit une solution simple et multiplateforme. Pour les applications spécifiques à Windows nécessitant une compatibilité COM, les appels directs à l'API offrent plus de contrôle. Les GUIDs séquentiels optimisent les performances de base de données mais sacrifient une certaine aléatoireté. Les UUIDs déterministes permettent aux systèmes distribués de générer des identifiants cohérents de manière indépendante. Validez toujours les GUIDs externes et envisagez la mise en cache pour les scénarios haute performance.
Le développement Delphi moderne bénéficie du support robuste des GUID intégré dans la RTL, avec des capacités supplémentaires disponibles via les API Windows et les implémentations personnalisées. Que vous construisiez des applications de bureau avec VCL, des applications multiplateformes avec FireMonkey, ou des applications serveur, les techniques présentées dans ce guide fournissent une base solide pour l'implémentation d'UUID. N'oubliez pas de considérer la sécurité des threads, les implications de performance et les exigences de stockage lors de la conception de votre stratégie UUID. Pour les applications de base de données, évaluez si les GUIDs séquentiels offrent des avantages de performance significatifs pour votre cas d'utilisation spécifique.
Pour une génération rapide d'UUID sans écrire de code, vous pouvez toujours utiliser notre outil de génération d'UUID en ligne, qui fournit une génération instantanée d'UUID dans divers formats d'un simple clic.