Как перебрать инициализированные перечислимые типы с Delphi 6 и избежать ошибки «вне границ»? - PullRequest
7 голосов
/ 29 сентября 2010

Я использую Delphi 6 Professional.Я взаимодействую с библиотекой DLL, которая объявляет перечислимый тип следующим образом:

TExtDllEnum = (ENUM1 = $0, ENUM2 = $1, ENUM3 = $2, ENUM4 = $4, ENUM5 = $8, ENUM6 = $10);

Как видите, инициализированные значения не являются смежными.Если я попытаюсь повторить тип с помощью цикла for следующим образом:

var
    e: TExtDllEnum;
begin
    for e := Low(TExtToDllEnum) to High(TExtToDllEnum) do
    ... // More code
end;

Delphi по-прежнему увеличивает e на 1 при каждом вызове цикла и тем самым создает числовые значения для e, которые не являются членами перечисляемого типа (например,, «3»), что приводит к ошибке «вне границ».Как можно перебрать перечислимый тип в цикле for, который генерирует только допустимые значения для перечислимого типа?

Спасибо.

Ответы [ 4 ]

13 голосов
/ 29 сентября 2010

Определяя набор констант ...

type
  TExtDllEnum = (ENUM1 = $0, ENUM2 = $1, ENUM3 = $2, ENUM4 = $4, ENUM5 = $8, ENUM6 = $10);

const
  CExtDllEnumSet = [ENUM1, ENUM2, ENUM3, ENUM4, ENUM5, ENUM6];


var
  e: TExtDllEnum;
begin
  e := Low(TExtDllEnum);
  while e <= High(TExtDllEnum) do
  begin
    if e in CExtDllEnumSet then 
      WriteLn(Ord(e));

    Inc(e);
  end;

  ReadLn;
end.

и реализован как итератор - просто для удовольствия ...

type
  TExtDllEnum = (ENUM1 = $0, ENUM2 = $1, ENUM3 = $2, ENUM4 = $4, ENUM5 = $8, ENUM6 = $10);
const
  CExtDllEnumSet = [ENUM1, ENUM2, ENUM3, ENUM4, ENUM5, ENUM6];

type
  TMyIterator = class
  private
    FValue: TExtDllEnum;
  public
    constructor Create;
    function Next: TExtDllEnum;
    function HasNext: Boolean;
  end;

  constructor TMyIterator.Create;
  begin
    FValue := Low(TExtDllEnum);
  end;

  function TMyIterator.HasNext: Boolean;
  begin
    Result := FValue <= High(TExtDllEnum);
  end;

  function TMyIterator.Next: TExtDllEnum;
  begin
    Result := FValue;

    repeat
      Inc(FValue);
    until (FValue in CExtDllEnumSet) or (FValue > High(TExtDllEnum))
  end;

var
  MyIterator: TMyIterator;
begin
  MyIterator := TMyIterator.Create;

  while MyIterator.HasNext do
    WriteLn(Ord(MyIterator.Next));

  MyIterator.Free;

  ReadLn;
end.
7 голосов
/ 29 сентября 2010

Насколько я помню, нет итераций так, как вы хотите. Если перечисление меняется не часто, возможно, можно обойти эту проблему, объявив «индексный массив», который позволяет выполнять итерацию так, как вы хотите. Хитрость в том, что вы перебираете не перечисление, а индекс, который вы в свою очередь можете «преобразовать» в допустимый элемент перечисления:

Я думаю, что могу лучше объяснить идею в коде:

const
  ExtDllEnumElements = 6;
  EnumIndexArray: array[0..ExtDllEnumElements - 1] of TExtDllEnum = (ENUM1, ENUM2, ENUM3, ENUM4, ENUM5, ENUM6);
var
  I: Integer;
begin
  for I := Low(EnumIndexArray) to High(EnumIndexArray) do
    WhateverYouWantWith(EnumIndexArray[I]);
end;
4 голосов
/ 29 сентября 2010

Когда вы определяете перечисление

TExtDllEnum = (ENUM1 = $0, ENUM2 = $1, ENUM3 = $2, ENUM4 = $4, ENUM5 = $8, ENUM6 = $10);

, вы фактически определяете тип перечисления, состоящий из 17 ($ 10 + 1) возможных значений.То есть задокументировано .

Существует много способов реализовать итерацию только для назначенных констант перечисления (см. Другие ответы), но, как вы делаете это, вы повторяете более 17 значенийи это нельзя изменить.

Вот еще один пример итерации, использующий тот факт, что все значения ENUMx, кроме ENUM1, имеют степени 2:

type
  TExtDllEnum = (ENUM1 = $0, ENUM2 = $1, ENUM3 = $2,
                 ENUM4 = $4, ENUM5 = $8, ENUM6 = $10);
var
    e: TExtDllEnum;

begin
  e:= Low(TExtDllEnum);
  repeat
    [..]
    if e = ENUM1 then e:= ENUM2
    else if e = High(TExtDllEnum) then Break
    else e:= e shl 1;
  until False;
end;
3 голосов
/ 29 сентября 2010

Вы не можете

если значения являются бинарными, попробуйте использовать цикл while, подобный этому

var
    e: TExtDllEnum;
begin
    e := 0;
    While e <= High(TExtToDllEnum) do
    begin
        ... // More code
        e := Power(2, e);
    end;

end;
...