[英]Delphi: Is it possible to enumerate all instances of a record (~typed constants) in the global namespace?
[英]Enumerate possible set values in Delphi
我在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;
我已經嘗試了這個,並且它可以工作,但感覺就像使用集合會更自然並且更好地使用類型系統(即使我要在所述類型系統外部枚舉集合)。
有沒有比枚舉基礎整數表示更好/更安全地枚舉所有可能的集合組合?
筆記:
我知道這個問題已經很老了,但這是我的偏好,因為它對我來說簡單而自然:
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;
嘗試
var EnumerationByte: Byte;
...
for EnumerationByte := 0 to 15 do begin
Options := TMyOptions(EnumerationByte);
end;
您的代碼無法編譯,因為您的枚舉(TMyOption)的值小於8,Delphi使用最小可能的大小(以字節為單位)。 因此,字節變量將適合您。
如果你有一個超過8但少於16個可能元素的集合,Word將起作用(而不是整數)。
對於超過16但少於32的DWord變量和類型轉換。
對於超過32個可能的元素,我認為更好的方法是使用字節數組或類似的東西。
問題是您正在嘗試轉換為set類型而不是枚舉類型。 您可以在整數和枚舉之間進行轉換,因為它們都是序數類型,但您不能轉換為集合,因為它們使用了您已經注意到的bitfiels。 如果您使用:
for EnumerationInteger := 0 to 15 do begin
Option := TMyOption(EnumerationInteger);
end;
它會工作,雖然不是你想要的。
幾個月前我遇到了同樣的問題,得出的結論是你無法枚舉Delphi中的一個集合的內容(至少在Delphi 7中),因為語言沒有在集合上定義這樣的操作。
編輯 :看來你甚至可以在D7中看到這個答案的咒語。
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;
從一個Integer到一個Set的轉換是不可能的,但是Tondrej曾經寫過一篇關於SetToString
和StringToSet
的博客文章 , SetToString
在SetOrdValue
方法中公開了你想要的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;
您的代碼將成為這樣:
for EnumerationInteger := 0 to 15 do begin
SetOrdValue(TypeInfo(TMyOptions), Options, EnumerationInteger);
end;
--jeroen
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.