Search code examples
delphienumsdelphi-7rtti

How to change my code to get the correct enumerate name value?


I аm trying to get the enumeration name value using RTTI.

My objective is to get the corresponding enumerate name value in Enum1(Tsex) from the selected enumerate name value in Enum2(iterator) using a string value.

Here is the code that I have implemented. I am using Delphi 7.

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,Dialogs,typinfo;

type

 Tsex = (homme,femme);
 iterator = (H,F);

TForm1 = class(TForm)
  procedure FormShow(Sender: TObject);
private
 { Déclarations privées }
 public
 { Déclarations publiques }
end;

var
  Form1: TForm1;

implementation

 {$R *.dfm}


 procedure TForm1.FormShow(Sender: TObject);
  var
    i : integer;
    OT: Tsex;
    FT: iterator;
 begin
   i:=0;
   OT := Low(Tsex);
   for FT := Low(iterator) to High(iterator) do
     if GetEnumName(TypeInfo(iterator), Ord(FT)) = 'F' then
     begin
       showmessage(GetEnumName(TypeInfo(Tsex), Ord(OT)));
     end;
     i:=i+1;
     OT:=Succ(OT);
   end;

When I use H as a string I get homme, but when I use F I also get homme. But it needs to be femme.


Solution

  • Problem:

    The problem in your code is that you are missing a begin after for, and this causes increment of i and assignment of OT to happen after the iteration is complete.

    What you need to change is:

    var
      i : integer;
      OT: Tsex;
      FT: iterator;
    begin
      i:=0;
      OT := Low(Tsex);
      for FT := Low(iterator) to High(iterator) do
      begin // <- Add begin here
        if GetEnumName(TypeInfo(iterator), Ord(FT)) = 'F' then
        begin
          showmessage(GetEnumName(TypeInfo(Tsex), Ord(OT)));
        end;
        i:=i+1;
        OT:=Succ(OT);
      end;
     end; // <- Add end; here
    

    Alternative solutions:

    As David has pointed out, it is better to use an array to map another set of values to your enum. Like this:

    type
     TSex = (homme, femme);
    
    const
     SexDBValues: array [TSex] of string =
     ('H', 'F');
    
    type
      TForm1 = class(TForm)
      procedure FormShow(Sender: TObject);
      private
      public
      end;
    
    var
      Form1: TForm1;
    
    implementation
    
    {$R *.dfm}
    
    function GetMyEnumValue(const aDBValue: string): TSex;
    var
      value: TSex;
    begin
      for value := Low(TSex) to High(TSex) do
      begin
        if SameText(SexDBValues[value], aDBValue) then
        begin
          Result := value;
          Exit;
        end;
      end;
    end;
    
    procedure TForm1.FormShow(Sender: TObject);
    var
      value: TSex;
    begin
      value := GetMyEnumValue('H');
      ShowMessage(GetEnumName(TypeInfo(TSex), Ord(value)));
    end;
    

    And when your enum type contains only two values, and is unlikely to have additional values in future, you can just use good old if-else operator:

    function GetMyEnumValue(const aDBValue: string): TSex;
    begin
      if SameText(aDBValue, 'F') then
      begin
        Result := femme;
      end else
      begin
        Result := homme;
      end;
    end;
    

    In few words, avoid overengineering problems.

    Note: We are using string to store the character value and SameText to compare it, as it compares text case-insensitively. Plus, it allows you to compare text of multiple characters, if in future you change your mind on how values are stored in DB.


    Advice:

    I would also recommend you to consult with Delphi Coding Style Guide.

    It might seem unrelated to problem, but following good practice on indentation helps to avoid such problems.

    Guidelines on naming types and variables are also important. They will similarly save you in other situations.