gpt4 book ai didi

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

转载 作者:行者123 更新时间:2023-12-03 15:30:18 27 4
gpt4 key购买 nike

我想要实现的就像虚拟代码一样:

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 我可以添加代码在运行时检查)

所以真正的问题是:

  1. 我应该为 EnumerationType 使用哪种类型?

  2. 如果不可能,还有其他使用枚举的解决方案吗?

  3. 或者任何解决方案可以提供合规时间检查以及灵活的结构?

编辑:

感谢 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如何接受Command1Command2Command3

更具体地说,delphi可以仅限制Command1Command2Command3的空间吗?意味着如果我输入 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 中,absolute 指令会导致编译器内部错误(在西雅图,它可以 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;

关于delphi - 是否可以将枚举类型作为参数传递并在其他函数中重用该类型?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39242690/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com