如何通过变量名称(字符串)访问变量?

7

我有一些全局字符串变量。

我需要创建一个函数,可以将它们传递并存储在某个结构中。 之后,我需要枚举它们并检查它们的值。

如何轻松实现这一点?

(我认为我需要某种反射,或者存储指针数组)。 无论如何,任何帮助都将不胜感激。

谢谢!


1
Delphi有RTTI,Java和.NET有Reflection。当谈论Delphi时,应该称其为RTTI,而不是Reflection。 - Cosmin Prund
@Cosmin 为什么?RTTI 是一种反射形式。只有当你知道这个缩写时,RTTI 才有意义。OP 正在寻求反射,不关心是使用 RTTI 还是其他机制实现的。 - David Heffernan
7
@David,因为可能有些人是Delphinian,他们知道RTTI是什么,但不知道Reflection是什么。因为文档把它叫做RTTI。因为谷歌对于“Delphi reflection”的唯一有用的结果都提示你应该称其为“RTTI”。 - Cosmin Prund
1
@Cosmin 如果人们知道什么是RTTI但不知道什么是反射,那么这些人需要拓宽他们的视野。反射是一个通用术语,而RTTI是反射的一种具体实现。 - David Heffernan
3个回答

7

首先,您无法使用Delphi的RTTI来实现此目的,因为Delphi 7的RTTI仅涵盖类的已发布成员。即使您使用Delphi XE,全局变量仍然没有RTTI(因为RTTI与类型而非“单元”相关联)。

唯一可行的解决方法是创建自己的变量注册表,并使用名称和指向变量本身的指针注册全局变量。

示例:

unit Test;

interface

var SomeGlobal: Integer;
    SomeOtherGlobal: string;

implementation
begin
  RegisterGlobal('SomeGlobal', SomeGlobal);
  RegisterGlobal('SomeOtherGlobal', SomeOtherGlobal);
end.

RegisterXXX类型需要在某个地方定义,可能是在它们自己的单元中,像这样:

unit GlobalsRegistrar;

interface

procedure RegisterGlobal(const VarName: string; var V: Integer); overload;
procedure RegisterGlobal(const VarName: string; var V: String); overload;
// other RegisterXXX routines

procedure SetGlobal(const VarName: string; const Value: Integer); overload;
procedure SetGlobal(const VarName:string; const Value:string); overload;
// other SetGlobal variants

function GetGlobalInteger(const VarName: string): Integer;    
function GetGlobalString(const VarName:string): string;
// other GetGlobal variants

implementation

// ....

end.

1
使用 var 参数而不是指针会使调用者的接口更加友好。 - David Heffernan
“var”参数也可以允许重载。但这仅适用于“RegisterXXX”和“SetXXX”,Getter需要是普通函数。 - Cosmin Prund
1
@John,考虑完全不使用全局变量,而是使用全局类的已发布属性(这样您就可以使用RTTI)。或者干脆不使用任何变量,像TOndrej建议的那样使用名称-值对。 - Cosmin Prund
@John,使用全局变量不是好的设计方式,考虑这个机会来重构你的代码。但请确保持有已发布属性(或命名值对)的类被作为参数传递给你的程序。 - Cosmin Prund
我个人更喜欢将所有需要的变量封装在一个类中,并使用公开属性。 - TLama
显示剩余2条评论

7

1
这是 Delphi 7 中唯一易于实现的解决方案,但它从来都不是一个好的解决方案:仅为使用TString的键=值查找而将东西存储为字符串是低效的。还有一个选项是仅对索引使用TStringList,并在单独的数组中存储实际值。 - Cosmin Prund
@Cosmin,你的意思是说当前的TStringList实现效率低下。我同意,但对于小型列表来说,它仍然是一个简单的解决方案,应该是可以接受的。如果性能成为问题,你可以使用IniFiles中的THashedStringList或类似的后代。 - Ondrej Kelle
还有一个问题是将所有内容都存储为字符串,因为TStringList.Value[]是一个字符串。这就是为什么我建议使用混合实现,其中TStringList(或TStringHash)仅用于索引,而使用基本类型的普通数组用于实际值存储。 - Cosmin Prund
你可以将变量内容存储在TStringList.Objects[]中,存储一个指针、包含值的对象或者一个(variant)记录。 - boileau

3
在Delphi 7上,我会按照Cosmin的想法进行接口设计,并使用基于Julian Bucknall的出色数据结构代码的字典类型来实现,链接为ezDSL
像XE这样的后续版本不仅具有更高级的RTTI,而且还包括一个非常棒的字典类型,使用泛型,因此字典可以包含任何您喜欢的类型。esDSL字典很容易使用,但由于它是指针类型,所以它不像Delphi泛型字典那样类型安全。
由于您需要在非常快的时间内查找字符串“变量名称”(我们称之为O(1)),因此您需要一个字符串到变量的字典。您可以将Strings用作键,并将Variants用作字典中的值,并摆脱原始全局变量,或者您可以尝试一些相当复杂的指向全局变量的指针逻辑,但我真的认为您最好使用简单的<string,variant>键值对字典。

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