Simplification de la Conversion entre les Types Énumérés et les Strings en Delphi
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:
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:
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:
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 ?