【问题标题】:Iterate through items in an enumeration in Delphi在 Delphi 中遍历枚举中的项目
【发布时间】:2009-10-21 12:45:47
【问题描述】:

我想遍历枚举中的项目。

我希望能够这样说:

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。 感谢您提供任何见解。

【问题讨论】:

    标签: delphi enums


    【解决方案1】:

    简单:

    type
      TWeekdays = (wdMonday, wdTuesday, wdWednesday, wdThursday, wdFriday);
    
    procedure Test;
    var
      el: TWeekdays;
    begin
      for el := Low(TWeekdays) to High(TWeekdays) do
        ; //
    end;
    

    【讨论】:

    • 好吧,我是个白痴。我早些时候尝试过,但我将“el”作为整数留下,并没有意识到我的错误。谢谢。
    • MaxEnum 可能有问题;返回超过枚举的实际数量。 GetEnumName 使用该错误结果来分析代码中的内部结构。你得到的是内部结构之外的随机内存。
    • 没错。您真正想要的是 High() 编译器魔术函数,如本回复所示。
    • GetEnumName 相信您正在传递一个有效值。它首先读取 TTypeData.NameList 字段,然后是 EnumUnitName 字段。之后是垃圾:函数解释为字符串长度的一个字节值,加​​上函数认为是字符串内容的许多字符。你很幸运,你只有两个额外的字符串。要从 PTypeInfo 确定最大枚举值,请阅读 MaxValue 字段:GetTypeData(TypeInfo(TWeekdays)).MaxValue。该字段对所有序数类型都有效。
    • @Z80 即使使用 Delphi 11 你也不能做到for el in TWeekdays do
    【解决方案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 函数只增减一,不检查不连续的定义方式,因此分配超出范围的值,同时也松散了定义的顺序

    诀窍:

    • 用正确顺序的元素声明一个连续的常量数组
    • 在该常量数组上循环

    如果你尝试这种其他(人类的逻辑思维方式),它不会起作用(无论是否使用 for 循环、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 上测试的。

    【讨论】:

    • 为什么人们继续用不连续的枚举来打自己的脚?
    • @UliGerhardt:我有这样的案例。我正在使用 Windows API 函数来打开串行连接。那里使用的波特率被定义为没有适当顺序的枚举。现在我有同样的问题,必须处理它。
    【解决方案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,以便您可以在 Delphi 的“for ... in”语句中使用它。 但是,当时它有时会产生内部编译器错误。

      编辑:

      设法让它在 Delphi 2007 及更高版本中工作,请参阅 this blog article(以及它下面的非常有趣的讨论)。

      【讨论】:

      【解决方案5】:

      此示例在 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)

      【讨论】:

      • 但要注意这些值之间的差距:TBlaBlub=(Bla=1, Blub=3);
      猜你喜欢
      • 2011-05-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-12-12
      • 2011-09-01
      • 2014-07-26
      • 2015-08-13
      • 1970-01-01
      相关资源
      最近更新 更多