Je travaille actuellement avec des types énumérés en Delphi et je me retrouve souvent à écrire des "record helpers" pour faciliter la conversion entre ces types énumérés et les chaînes de caractères (string). Par exemple, pour un type énuméré représentant différents modèles de conception, je crée un helper pour ajouter des méthodes comme ToString, FromString et ToArray.

Voici un exemple de code pour illustrer mon propos :

Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
type
  TMyEnumtype = (abstract_factory, builder, factory_method, prototype, singleton);
  TMyEnumtypeHelper = record helper for TMyEnumtype
    function ToString: string;
    function FromString(Value: string): TMyEnumtype;
    class function ToArray(CaseRespect: Boolean = False): TArray<string>; static;
  end;
Ma question est la suivante : Existe-t-il une façon plus native ou générique en Delphi pour obtenir la conversion entre un type énuméré et une chaîne de caractères, sans avoir à écrire un "record helper" spécifique pour chaque type énuméré ? Autrement dit, y a-t-il un moyen d'éviter ces manipulations répétitives et de rendre le code plus concis et plus maintenable ?

A défaut, j'ai développé un record helper générique :

Code : Sélectionner tout - Visualiser dans une fenêtre à part
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
unit System.Generics.TypeEnum;
 
interface
 
uses
  System.SysUtils, System.Generics.Collections, System.TypInfo, System.StrUtils,
  System.Classes;
 
type
  TEnumHelper<T: record> = record
  private
    class var FLowerCaseEnumNames: TDictionary<string, T>;
    class constructor Create;
    class destructor Destroy;
  public
    class function ToString(Value: T): string; static;
    class function FromString(const Value: string): T; static;
    class function ToArray(CaseRespect: Boolean = False): TArray<string>; static;
  end;
 
implementation
 
{ TEnumHelper<T> }
 
class constructor TEnumHelper<T>.Create;
var
  EnumType: PTypeInfo;
  EnumData: PTypeData;
  EnumValue: Integer;
begin
  EnumType := TypeInfo(T);
  if EnumType^.Kind <> tkEnumeration then
    raise EInvalidOperation.Create('TEnumHelper can only be used with enumeration types');
 
  EnumData := GetTypeData(EnumType);
  FLowerCaseEnumNames := TDictionary<string, T>.Create;
  for EnumValue := EnumData^.MinValue to EnumData^.MaxValue do
  begin
    var EnumName := GetEnumName(EnumType, EnumValue);
    var Enum: T;
    Move(EnumValue, Enum, SizeOf(T));
    FLowerCaseEnumNames.AddOrSetValue(AnsiLowerCase(EnumName), Enum);
  end;
end;
 
class destructor TEnumHelper<T>.Destroy;
begin
  FLowerCaseEnumNames.Free;
end;
 
class function TEnumHelper<T>.ToString(Value: T): string;
begin
  Result := GetEnumName(TypeInfo(T), Integer(PByte(@Value)^));
end;
 
class function TEnumHelper<T>.FromString(const Value: string): T;
begin
  if not FLowerCaseEnumNames.TryGetValue(AnsiLowerCase(Value), Result) then
    raise EArgumentException.CreateFmt('%s : Non-existent enumerated type', [Value]);
end;
 
class function TEnumHelper<T>.ToArray(CaseRespect: Boolean): TArray<string>;
var
  EnumType: PTypeInfo;
  EnumData: PTypeData;
  EnumValue: Integer;
begin
  EnumType := TypeInfo(T);
  EnumData := GetTypeData(EnumType);
  SetLength(Result, EnumData^.MaxValue - EnumData^.MinValue + 1);
  for EnumValue := EnumData^.MinValue to EnumData^.MaxValue do
  begin
    if CaseRespect then
      Result[EnumValue - EnumData^.MinValue] := GetEnumName(EnumType, EnumValue)
    else
      Result[EnumValue - EnumData^.MinValue] := AnsiLowerCase(GetEnumName(EnumType, EnumValue));
  end;
end;
 
 
end.
Ce code me permet d'implémenter mon TMyEnumtypeHelper (ci-dessus) de la façon suivante :

Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{ TMyEnumtypeHelper }
 
function TMyEnumtypeHelper.FromString(Value: string): TMyEnumtype;
begin
  Self := TEnumHelper<TMyEnumtype>.FromString(Value);
  Result := Self;
end;
 
class function TMyEnumtypeHelper.ToArray(CaseRespect: Boolean = False): TArray<string>;
begin
  Result := TEnumHelper<TMyEnumtype>.ToArray(CaseRespect);  
end;
 
function TMyEnumtypeHelper.ToString: string;
begin
  Result := TEnumHelper<TMyEnumtype>.ToString(Self);
end;
Mais cela semble à mon goût du bricolage. Auriez-vous des pistes plus judicieuses ?