我在使用DWScript中遇到了元类(meta classes)的问题。
我们使用脚本编写使VAR和终端用户可以自定义我们的应用程序。
我们的应用数据基本上由许多小对象组成的树形结构。每个对象可以是“愚笨”的,只显示数据,也可以是智能的。通过将不同的脚本类与树对象关联来实现智能功能。
我遇到的问题是,脚本需要与Delphi侧框架通信,告诉它要使用什么脚本类来实现对象。基本上,我需要将脚本元类传递到Delphi侧,并以可以安全持久化的格式(可能是类型名称字符串)存储信息。我还需要能够沿着相反的方向进行操作;即从Delphi侧返回元类到脚本。
TdwsUnit声明
type
// Base class of all tree objects
TItem = class
...
end;
// The meta class
// This is actually declared in code since TdwsUnit doesn't have design time support for meta classes.
// Shown here for readability.
TItemClass = class of TItem;
// The procedure that passes the meta class to the Delphi side.
// I cannot use a TItemClass parameter as that isn't declared until run time (after the TdwsUnit has initialized its tables).
procedure RegisterItemClass(AClass: TClass);
脚本
type
TMyItem = class(TItem)
...
end;
begin
// Pass the meta class to the Delphi side.
// The Delphi side will use this to create a script object of the specified type
// and attach it to the Delphi side object.
RegisterItemClass(TMyItem);
end;
Delphi实现
TItemClass
是元类的声明,它在TdwsUnit.OnAfterInitUnitTable
中完成。
procedure TMyDataModule.dwsUnitMyClassesAfterInitUnitTable(Sender: TObject);
var
ItemClass: TClassSymbol;
MetaClass: TClassOfSymbol;
begin
// Find the base class symbol
ItemClass := dwsUnitMyClasses.Table.FindTypeLocal('TItem') as TClassSymbol;
// Create a meta class symbol
MetaClass := TClassOfSymbol.Create('TItemClass', ItemClass);
dwsUnitMyClasses.Table.AddSymbol(MetaClass);
end;
RegisterItemClass
实现
procedure TMyDataModule.dwsUnitMyClassesFunctionsRegisterItemClassEval(info: TProgramInfo);
var
ItemClassSymbol: TSymbol;
ItemClassName: string;
begin
ItemClassSymbol := TSymbol(Info.Params[0].ValueAsInteger);
ItemClassName := ItemClassSymbol.Name;
...
end;
问题是如何从元类参数获取TSymbol?编辑:我在这个旧问题中找到了解决问题的一部分。
简而言之,解决方案是将参数值强制转换为
TSymbol
:
然而...
现在假设我将类名存储为字符串。 如何从此类名返回符号? 我需要这样做,因为就像脚本可以设置项目类(使用上面的代码)一样,脚本也可以要求项的类。
我尝试使用任何四种不同方法中的符号表来查找我需要的符号,但没有一种能够找到该符号。
var
ItemClassName: string;
ItemClassSymbol: TSymbol;
...
ItemClassName := 'TMyItem';
...
ItemClassSymbol := Info.Table.FindTypeSymbol(ItemClassName, cvMagic);
if (ItemClassSymbol = nil) then
ItemClassSymbol := Info.Table.FindSymbol(ItemClassName, cvMagic);
if (ItemClassSymbol = nil) then
ItemClassSymbol := Info.Table.FindTypeLocal(ItemClassName);
if (ItemClassSymbol = nil) then
ItemClassSymbol := Info.Table.FindLocal(ItemClassName);
// ItemClassSymbol is nil at this point :-(
所以问题是:如何从Delphi端获取对应的TSymbol,给定在脚本中声明的元类名?
编辑:我现在已经找到了最后一部分的一个可能的解决方案。
以下内容似乎有效,但我不确定这是否是正确的方法。我原以为我需要将符号搜索范围限制在当前脚本单元内。
var
ItemClassName: string;
ItemClassSymbol: TSymbol;
...
ItemClassName := 'TMyItem';
...
ItemClassSymbol := Info.Execution.Prog.RootTable.FindSymbol(ItemClassName, cvMagic);
if (ItemClassSymbol = nil) then
raise EScriptException.CreateFmt('ItemClass not found: %s', [ItemClassName]);
Info.ResultAsInteger := Int64(ItemClassSymbol);
Info.Vars ['TMyItem'] .GetConstructor
。相反,我不得不调用受保护的TProgramInfo.GetSymbolInfo
来创建IInfo对象,并在其中调用构造函数(这反过来导致我遇到了这个错误)。 - SpeedFreak