当前位置 : 主页 > 编程语言 > delphi >

delphi – 是否可以将枚举类型作为参数传递并在其他函数中重用此类型?

来源:互联网 收集:自由互联 发布时间:2021-06-23
我想要实现的就像虚拟代码: type CommandSetOne = (Command1, Command2, Command3); CommandSetTwo = (Command4, Command5, Command6); TRobot = class procedure RegisterCommands(anyEnumerationType : TRttiEnumerationType); procedure Exe
我想要实现的就像虚拟代码:

type
  CommandSetOne = (Command1, Command2, Command3);
  CommandSetTwo = (Command4, Command5, Command6);

  TRobot = class
    procedure RegisterCommands(anyEnumerationType : TRttiEnumerationType);
    procedure ExecuteCommands(anEnumeration : theEnumerationType);
  end;

我可能有多组命令,命令集中的任何命令都是可替换的.

TRobot有一个过程可以将枚举类型作为参数,他将保存此类型,将此类型用于ExecuteCommands过程.

关于传递任何枚举类型作为参数,我发现一种方法是使用TRttiEnumerationType,在调用方面它应该如下所示:

var
  rttiContext : TRttiContext;
  typeref : TRttiType;
  RobotA : TRobot;
begin
  rttiContext := TRttiContext.Create();
  RobotA := TRobot.Create();
  RobotA.RegisterCommands(rttiContext.GetType(TypeInfo(CommandSetOne)));
end;

但我坚持传递Command1之类的命令.我已经为theEnumerationType尝试了Variant,但似乎我无法将Command1传递给它.

我知道如果我使用类似TStringList的东西,这是一个更容易的方法来做我想要的,但我想在符合时间的情况下通过delphi进行检查,以防我输入错误的命令(使用TstringList我可以添加代码来检查运行)

所以真正的问题是:

>我应该为theEnumerationType使用哪种类型?
>如果不可能,使用Enumeration的任何其他解决方案?
>或任何解决方案可以提供符合时间检查以及灵活的结构?

编辑:

感谢David建议,我应该使用两个Rtti的东西,所以为了说清楚,我添加了RegisterCommands的实现

implementation
  procedure TRobot.RegisterCommands(anyEnumerationType : TRttiEnumerationType);
    begin
    theEnumerationType := anyEnumerationType;
    end;
  procedure TRobot.ExecuteCommands (anyEnumerationValueoftheType : ???);
    begin
    //do something with the command
    end;

什么适合该类型的任何枚举值?

例如,如果我在RegisterCommands中使用CommandSetOne,
delphi如何接受Command1或Command2或Command3?

更具体地说,delphi只能为Command1或Command2或Command3限制空间吗?意味着如果我把Command4它给我一个编译错误?

每当你发现自己想要将某种类型的东西作为参数传递时,goto解决方案就是泛型.

我们将滥用枚举实际上是一个整数不足的事实.
假设您在枚举标签的字符串表示中编码了实际命令.
例如

TCommands = (Left, Right, Up, Down);

TRobot = class
private
  FRegisteredCommands: TDictionary<integer, string>;
public  
  procedure RegisterCommand<E: record>(Enum : E);
  procedure ExecuteCommand<E: record>(Enum : E);
end;

procedure TRobot.RegisterCommand<E: record>(Enum: E);
var
  Key: integer absolute Enum;  //typesafe, because of the if below.  
  Info: PTypeInfo;
begin
  if GetTypeKind(E) <> tkEnumeration then raise Exception.Create('Enum is not an enum');
  //Added type safety:
  if     not(TypeInfo(E) = TypeInfo(TRobotCommand1)) 
      or not(TypeInfo(E) = TypeInfo(TRobotCommend2)) then raise ....
  Info:= TypeInfo(Enum);
  FRegisteredCommands.Add(Key, GetEnumName(Info, Key));
end;

The compiler will remove all this if code if these checks are true and only generate the code if these checks are false, because GetTypeKind is a compiler intrinsic routine这意味着执行这些检查需要零运行时间
请注意,如果疯狂快速性能是你的事情,你可以使用if TypeInfo(E)= TypeInfo(TMyCommandSet)编译器内在技巧来硬编码命令.

请注意,在早期的Delphi中,绝对指令会导致编译器内部错误(在西雅图,它可以100%正常工作).在这种情况下,更改代码如下:

procedure TRobot.RegisterCommand<E: record>(Enum: E);
var
  Key: integer;
  Info: PTypeInfo;
begin
  ....
  Key:= PInteger(@Enum)^;
  .....

如果给定的TRobot后代只接受单一类型的命令,那么我将泛型类型移动到TRobot,如下所示:

TBaseRobot<E: record> = class(TObject)
   constructor Create; virtual;
   procedure RegisterCommand(Enum: E);  //only implement once, see above.
   procedure ExecuteCommand(Enum: E); virtual; abstract; //implement in descendents.
....

constructor TBaseRobot<E>.Create;
begin
  inherited Create;
  if GetTypeKind(E) <> tkEnumeration then raise('error: details');  
end;

TRobotA = class(TBaseRobot<TMyEnum>)
  procedure ExecuteCommand(Enum: TMyEnum); override;
end;
....

编辑
您可以在类构造函数中执行此操作,而不是在构造函数中执行检查.这样做的好处是,一旦您的应用程序启动,任何错误都会触发,而不是在您的测试中可能永远不会发生的某个随机时间.

删除构造函数并将其替换为类构造函数,如下所示:

//You should never name a class constructor `create`. class constructor don't create anything, they init stuff.
class constructor TBaseRobot<E>.Init;  
begin
  if GetTypeKind(E) <> tkEnumeration then raise('error: details');  
end;
网友评论