在Delphi中传递不同枚举类型的混合体。

5

我需要编写一个过程,可以传递不同的枚举选择。

type
  TEnumOne = (eOneFlagOne, eOneFlagTwo);
  TEnumTwo = (eTwoFlagOne, eTwoFlagTwo);

这个方法应该接受不同的枚举类型:

Process([eOneFlagOne, eTwoFlagTwo]);

我正在尝试这样实现:
// does not work!
procedure Process(const Enums: array of Variant);
var aValue; Variant
begin
  for aValue in Enums do
  begin
    // of course, can't work...
    if aValue is TEnumOne then
  end; 
end;

那么,除了Variant以外,我有没有可以选择的类型呢?或者有没有不同的方法,我没有看到呢?


刚好记得,如果我没记错的话,您可以将枚举类型转换为整数类型,例如 I := Integer(eOneFlagOne);F := TEnumOne(I); 在这种情况下,如果 eOneFlagOne = 0,那么 eTwoFlagOne = 0。除非你还想要一个类似于0,1/2,3而不是0,1/0,1的唯一标识符,那我就不知道了。 - Jerry Dodge
1
或者,作为替代方案,可以使用常量,例如 const E_ONE_FLAG_ONE = 0; E_ONE_FLAG_TWO = 1; E_TWO_FLAG_ONE = 2; E_TWO_FLAG_TWO = 3; 等等,而不是枚举。 - Jerry Dodge
顺便说一句,我相信RRUZ询问版本的原因可能是因为更新的版本(如XE2)可能提供更简单的方法来完成这个任务... - Jerry Dodge
4
你的设计听起来有缺陷。与其和语言斗争,不如重新考虑设计。就这个例子而言,如果你要写像“if aValue is TEnumOne”这样的代码,显然需要使用类而不是枚举。这些类可以持有枚举值。 - David Heffernan
我正在使用 Delphi XE。问题是,枚举类型由 RemObjects 自动生成为接口。因此,我无法更改枚举类型的定义。 - Francois Zbinden
请在您的问题中加入一条提醒,说明这些类型定义是由RemObjects自动创建的,您无法更改其定义方式。 - Jerry Dodge
4个回答

10

见证帕斯卡之美。

这是一个可以正常工作的例子,很可能是您想要尝试的:

program Project34; {$APPTYPE CONSOLE}

type
  TEnum=(eOneFlagOne,eOneFlagTwo,eTwoFlagOne,eTwoFlagTwo);
  TEnumSet=set of TEnum;

const
  cEnumOne=[eOneFlagOne,eOneFlagTwo];
  cEnumTwo=[eTwoFlagOne,eTwoFlagTwo];

procedure Process(const Enums: TEnumSEt);
var e:TEnum;
begin
  for e in Enums do
    WriteLn(ord(e));
end;

begin
  Process([eOneFlagOne, eTwoFlagTwo]);
  Process(cEnumOne);
  Process(cEnumTwo);
end.

请注意,您也可以这样声明常量。也许这更清晰:

const
  cEnumOne:TEnumSet=[eOneFlagOne,eOneFlagTwo];
  cEnumTwo:TEnumSet=[eTwoFlagOne,eTwoFlagTwo];

要完成此操作,您需要使用TEnumOne = eOneFlagOne..eOneFlagTwo等。 - David Heffernan
遗憾的是,我无法更改或扩展枚举类型的定义。 我也无法构建集合。 - Francois Zbinden
@FrancoisZbinden 如果您无法更改枚举类型定义,也无法构建集合,则应更改Process函数,使其接受两个参数:Process(EnumOneArray:TEnumOne数组; EnumTwoArray:TEnumTwo数组),然后在两个数组中检查条件。 - bjaastad_e

4
坦率地说,当您开始尝试弯曲您的语言时,通常意味着您的方法可能是错误的(并非总是如此)。我很想听听您要解决的问题,也许有更好的设计选项。
据我们所知,建议您使用两个具有不同签名的函数来解决问题。或者,如果所需的逻辑分支足够相似,则可以创建一个通用方法(假设使用Delphi 2009或更高版本),并将枚举类型用作通用参数。
...
procedure Process<T>(const enumParam : T) // Add a generic constraint here as well
begin
...
end;

对我来说,似乎使用这两种不同的方法可能是最佳选项(或者完全使用其他方法)。


1

RRUZ 删除了他的回答,这是一个带有类型安全的重新制作版本。 RTTI 用于识别不同的枚举常量。

function EnumToString(const TypeInfo : pTypeInfo; Ix : Integer) : string;
begin
  Result := GetEnumName(TypeInfo, ix); 
end;

procedure Process( const Args : array of string);
var
  LIndex,ix : integer;
  EnumOne : TEnumOne;
  EnumTwo : TEnumTwo;
begin
  for LIndex := 0 to High(Args) do begin
    ix := GetEnumValue( TypeInfo(TEnumOne), Args[LIndex]);
    if (ix <> -1) then
    begin
      EnumOne := TEnumOne( ix); 
      // do something with EnumOne
      ...
      continue;
    end;

    ix := GetEnumValue( TypeInfo(TEnumTwo), Args[LIndex]);
    if (ix <> -1) then
    begin
      EnumTwo := TEnumTwo( ix); 
      // do something with EnumTwo
      ...
      continue;
    end; 
    ... 
    etc

  end;        
end;

Process( [EnumToString(TypeInfo(TEnumOne),Ord(TEnumOne.eOneFlagOne)),
          EnumToString(TypeInfo(TEnumTwo),Ord(TEnumTwo.eTwoFlagTwo))]);

0

另一种选择是将其保留为一个枚举:

TEnum = (eOneFlagOne, eOneFlagTwo, eTwoFlagOne, eTwoFlagTwo);

然后,Process函数看起来会像这样:

procedure Process(Enums: Array of TEnum);
var 
  aValue: TEnum;
begin
  for aValue in Enums do
  begin
    if aValue in [eOneFlagOne, eOneFlagTwo] then
      // Handle the eOne enums
    else if aValue in [eTwoFlagOne, eTwoFlagTwo] then
      // Handle the eTwo enums
  end; 
end;

这只是重复了Wouter的答案。除非你添加一些新的东西,否则它只是杂乱无章的。 - David Heffernan
使用集合(就像Wouter所建议的)对于Pascal语言来说比开放式参数数组更本地化和高效。 - Arnaud Bouchez
@ArnaudBouchez 我同意。然而,从实际角度来看并不太重要。只有在函数是某个需要高度优化的核心循环的一部分时才会有影响。 - bjaastad_e

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接