在Delphi中枚举可能的设置值

时间:2010-11-04 00:06:28

标签: delphi set

我在Delphi中有一个计算算法,有许多不同的选项,我需要尝试各种选项组合以找到最佳解决方案。

TMyOption = (option1, option2, option3, option4);
TMyOptions = set of TMyOption;

我想知道使用Integer循环来枚举它们:

for EnumerationInteger := 0 to 15 do begin
    Options := TMyOptions(EnumerationInteger);
end;

这不编译。我想知道的是,是否有任何相当简单的方法可以从Integer转换为Set(Web上的大多数问题都试图从另一个方向转换,从Set到Integer),如果是这样,它是什么?

另一种可能性是将Integer用作位域:

C_Option1 = 1;
C_Option2 = 2;
C_Option3 = 4;
C_Option4 = 8;

然后使用按位和

测试成员资格
if (Options and C_Option2) > 0 then begin
    ...
end;

我已经尝试了这个,并且它有效,但感觉就像使用集合会更自然并且更好地使用类型系统(即使我要在所述类型系统外部枚举集合)。

枚举所有可能的集合组合比枚举基础整数表示更好/更安全吗?

注意:

  1. 我知道理论上不保证集合的整数值(尽管如果你不使用枚举编号,我怀疑它们在实践中)。
  2. 可能有四个以上的选项(是的,我知道它会以指数方式增长,如果有太多选项,算法可能会永远占用)。

6 个答案:

答案 0 :(得分:6)

我知道这个问题已经很老了,但这是我的偏好,因为这对我来说很简单和自然:

function NumericToMyOptions(n: integer): TMyOptions;
var
  Op: TMyOption;
begin
  Result:= [];
  for Op:= Low(TMyOption) to High(TMyOption) do
    if n and (1 shl ord(Op)) > 0 then Include(Result, Op);
end;

答案 1 :(得分:4)

尝试

var EnumerationByte: Byte;
...
for EnumerationByte := 0 to 15 do begin
    Options := TMyOptions(EnumerationByte);
end;

答案 2 :(得分:2)

您的代码无法编译,因为您的枚举(TMyOption)的值少于8,Delphi使用最小可能的大小(以字节为单位)。因此,字节变量将适合您。

如果你有一个超过8但少于16个可能元素的集合,Word将起作用(而不是整数)。

对于超过16但少于32的DWord变量和类型转换。

对于超过32个可能的元素,我认为更好的方法是使用字节数组或类似的东西。

答案 3 :(得分:0)

问题是您正在尝试转换为set类型而不是枚举类型。您可以在整数和枚举之间进行转换,因为它们都是序数类型,但您不能转换为集合,因为它们使用了您已经注意到的bitfiels。如果您使用:

for EnumerationInteger := 0 to 15 do begin
  Option := TMyOption(EnumerationInteger);
end;

它会起作用,虽然不是你想要的。

几个月前我遇到了同样的问题,得出的结论是你无法枚举Delphi中的一个集合的内容(至少在Delphi 7中),因为语言没有在集合上定义这样的操作。

修改:您似乎甚至可以在D7中看到此答案的内容。

答案 4 :(得分:0)

500 - 内部服务器错误的答案可能是最简单的。

另一种不太可能随着选项数量的变化而中断的方法是声明一个布尔数组,并打开/关闭它们。这比使用纯整数要慢。主要优点是,您不需要更改您使用的整数类型,如果您有超过32个选项,则可以使用它。

procedure DoSomething
var BoolFlags : Array[TOption] of Boolean;
    I: TOption;
  function GetNextFlagSet(var Bools : Array of Boolean) : Boolean;
  var idx, I : Integer;
  begin
    idx := 0;
    while Bools[idx] and (idx <= High(Bools)) do Inc(idx);

    Result := idx <= High(Bools);

    if Result then
      for I := 0 to idx do
        Bools[I] := not Bools[I];
  end;
begin
  for I := Low(BoolFlags) to High(BoolFlags) do BoolFlags[i] := False;

  repeat
    if BoolFlags[Option1] then
      [...]

  until not GetNextFlagSet(BoolFlags);
end;

答案 5 :(得分:0)

无法从整数转换为集合,但Tondrej曾在SetToStringStringToSet上写了blog article,在{{1}中公开了您想要的内容方法:

SetOrdValue

您的代码将变为:

uses
  TypInfo;

procedure SetOrdValue(Info: PTypeInfo; var SetParam; Value: Integer);
begin
  case GetTypeData(Info)^.OrdType of
    otSByte, otUByte:
      Byte(SetParam) := Value;
    otSWord, otUWord:
      Word(SetParam) := Value;
    otSLong, otULong:
      Integer(SetParam) := Value;
  end;
end;

- 的Jeroen