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;