开发者

Iterate through items in an enumeration in Delphi

I want to iterate through the items in an enumeration.

I'd like to be able to say something like this:

type
  TWeekdays = (wdMonday, wdTuesday, wdWednesday, wdThursday, wdFriday);

...
elementCount := GetElementCount(TypeInfo(TWeekDays));

for i := 0 to elementCount - 1 do begin
  ShowMessage(GetEnumName(TypeInfo(TWeekdays),i));
end;

The closest I've been able to come is thi开发者_高级运维s:

function MaxEnum(EnumInfo: PTypeInfo): integer;
const
  c_MaxInt = 9999999;
var
  i: integer;
  s: string;
begin
  //get # of enum elements by looping thru the names
  //until we get to the end.
  for i := 0 to c_MaxInt do begin
    s := Trim(GetEnumName(EnumInfo,i));
    if 0 = Length(s) then begin
      Result := i-1;
      Break;
    end;
  end;
end;

Which I use like this:

procedure TForm1.BitBtn1Click(Sender: TObject);
var
  i, nMax: integer;
begin
  ListBox1.Clear;
  nMax := MaxEnum(TypeInfo(TWeekdays));
  for i := 0 to nMax do begin
    ListBox1.Items.Add(GetEnumName(TypeInfo(TWeekdays),i));
  end;
end;

That works well, except the list I get looks like this (notice the last two items):

wdMonday
wdTuesday
wdWednesday
wdThursday
wdFriday
Unit1
'@'#0'ôÑE'#0#0#0#0#0#0#0#0#0#0#0#0#0  <more garbage characters>

The two items at the end are obviously not what I want.

Is there a better way to iterate through the elements of an enumerated type?

If not, then is it safe to assume that there will always be exactly two extra items using my current method? Obviously one is the Unit name... but what is the "@" symbol doing? Is it really garbage, or is it more type information?

I'm using Delphi 2007. Thanks for any insights.


Simple:

type
  TWeekdays = (wdMonday, wdTuesday, wdWednesday, wdThursday, wdFriday);

procedure Test;
var
  el: TWeekdays;
begin
  for el := Low(TWeekdays) to High(TWeekdays) do
    ; //
end;


It is quite more complex than that, when used special enumerations... let's see a really 100% working solution for a complex enumerated definition:

type
    TmyEnumType=(myEnumTypeA=5,myEnumTypeB=2,myEnumTypeC=9);
const
     myEnumTypeOrder:Array[1..3] of TmyEnumType=(myEnumTypeA,myEnumTypeB,myEnumTypeC);
procedure TForm1.Button1Click(Sender: TObject);
var
   myEnumTypeVariable:TmyEnumType;
begin
     myEnumTypeVariable:=Low(TmyEnumType);
     for myEnumTypeVariable in myEnumTypeOrder
     do begin
             ShowMessage(IntToStr(Ord(myEnumTypeVariable))); // Sample code to show enum integer value
             // Extra code you neede
        end;
end;
// This code shows three messages in this secuence: 5, 2, 9
//    Correct number of elements and in the correct order

Notes:

  • Not all enumerated definitions must start with 0 and be contiguous, can be defined as on the sample
  • See how the type has been defined (not sorted, not contiguous, etc...)
  • See how it has been added a constant array with the correct order of the elements
  • Must iterate on such array since order is lost and Succ and Pred do not work properly on that kind of enumerations

Why it has been done like that?:

  • Enumerated order must be conserved and it is not contiguous
  • Delphi when on that sample creates type TmyEnumType assign to it (9-2+1=8) elemments (from lower one (2) to higher one (9), so valid values for such type are from ordinal 2 to ordinal 9
  • Delphi Succ and Pred functions only increase and decrese by one, do not check for non-contiguous way of defining it, so assign values that are out of scope, and also loose the order of definition

The Trick:

  • Declare a contiguous constant array with elements in the correct order
  • Loop on that constant array

If you try this other (logical human way of thinking) it will not work (no matter if use for loop, while loop, repeat until, etc):

type
    TmyEnumType=(myEnumTypeA=5,myEnumTypeB=2,myEnumTypeC=9);
procedure TForm1.Button1Click(Sender: TObject);
var
   myEnumTypeVariable:TmyEnumType;
begin
     for myEnumTypeVariable:=Low(TmyEnumType) to High(TmyEnumType);
     do begin
             ShowMessage(IntToStr(Ord(myEnumTypeVariable))); // Sample code to show enum integer value
             // Extra code you neede
        end;
end;
// This code shows eight messages in this secuence: 2, 3, 4, 5, 6, 7, 8, 9
//    Inorrect number of elements and in order is lost

That is what i have tested by my own on Turbo Delphi 2006.


You can use Succ(x) and Pred(x) to loop through an enumeration. But remember to check for boundaries so you don't try Succ(x) on the last element in the enumeration!

Example:

type
  TWeekdays = (wdMonday, wdTuesday, wdWednesday, wdThursday, wdFriday);

procedure Test;
var
  val: TWeekdays;
begin
  val := wdTuesday;
  val := Succ(val); // wdWednesday

  val := wdFriday;
  val := Pred(val); // wdThursday, 
end;


This example compiles on Delphi Sydney (10.4)

 type
   TDay = (Mon=1, Tue, Wed, Thu, Fri, Sat, Sun);   // Enumeration values
 var
   day   : TDay;          // Enumeration variable
 begin
   for day := Mon to Fri do
      Caption:= IntToStr(ord(day));
 end;

( http://www.delphibasics.co.uk/Article.asp?Name=Sets )


I made an EnumerationEnumerator so you could use it in the 'for ... in' statement in Delphi. However, back then it would sometimes generate internal compiler errors.

Edit:

Managed to get it to work in Delphi 2007 and up, see this blog article (and the quite interesting discussion under it).

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜