Итерации через элементы в перечислении в Delphi

Я хочу перебирать элементы в перечислении.

Я хотел бы сказать что-то вроде этого:

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;

Ближайшим, с которым я смог приехать, является следующее:

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;

Что я использую так:

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;

Это хорошо работает, за исключением того, что список выглядит так (обратите внимание на два последних элемента):

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

Два элемента в конце, очевидно, не то, что я хочу.

Есть ли лучший способ итерации по элементам перечисляемого типа?

Если нет, то можно ли считать, что всегда будет два дополнительных элементов, используя мой текущий метод? Очевидно, что это имя группы... но что такое символ "@"? Это действительно мусор, или это больше информации о типе?

Я использую Delphi 2007. Спасибо за любые идеи.

Ответы

Ответ 1

Простой:

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

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

Ответ 2

Это сложнее, чем при использовании специальных перечислений... давайте посмотрим действительно 100% -ное рабочее решение для сложного перечисленного определения:

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

Примечания:

  • Не все перечисленные определения должны начинаться с 0 и быть смежными, могут быть определены как на образце
  • Посмотрите, как был определен тип (не отсортирован, не соприкасается и т.д.)
  • Посмотрите, как был добавлен постоянный массив с правильным порядком элементов
  • Нужно итерации на таком массиве, так как порядок потерян, а Succ и Pred не работают должным образом в таких перечислениях.

Почему это было сделано так?:

  • Перечисленный порядок должен быть сохранен и не соприкасается
  • Delphi, когда на этом образце создает тип TmyEnumType, присваивает ему (9-2 + 1 = 8) элементы (от нижнего (2) до более высокого (9), поэтому допустимые значения для такого типа относятся к порядку 2 к порядку 9
  • Функции Delphi Succ и Pred только увеличиваются и уменьшаются на единицу, не проверяйте для несвязанного способа его определения, поэтому присваивайте значения, выходящие за пределы области видимости, а также потеряйте порядок определения

Трюк:

  • Объявить непрерывный постоянный массив с элементами в правильном порядке
  • Петля на этом постоянном массиве

Если вы попробуете этот другой (логический человеческий образ мышления), он не будет работать (независимо от того, используете ли он цикл, while цикл, повторяйте до и т.д.):

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

Это то, что я проверил самостоятельно на Turbo Delphi 2006.

Ответ 3

Вы можете использовать Succ (x) и Pred (x) для прокрутки перечисления. Но не забудьте проверить границы, чтобы не попробовать Succ (x) в последнем элементе перечисления!

Пример:

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;

Ответ 4

Я сделал EnumerationEnumerator, чтобы вы могли использовать его в инструкции "for... in" в Delphi. Тем не менее, тогда он иногда генерирует внутренние ошибки компилятора.

Edit:

Чтобы заставить его работать в Delphi 2007 и выше, см. эту статью в блоге (и довольно интересная дискуссия).