如何在 DWScript 中公开动态记录数组?
How to expose a dynamic array of records in DWScript?
我已经在 DWScript 单元中声明了一个简单的记录类型:
TSampleRecord = record
name: string;
end;
如何将 Delphi 应用程序中的此类数组公开给脚本?
例如,Delphi 应用程序中的以下方法:
// Delphi side
function GetSampleRecordArray(): array of TSampleRecord;
必须可以从脚本访问:
// Script side
var myArray: array of TSampleRecord;
myArray := GetSampleRecordArray();
在 returns 动态记录数组的脚本中注册函数之前,您需要:
- 注册记录类型
- 注册该类型的动态数组
TdwsUnit
有辅助方法 ExposeRTTIDynamicArray
来为脚本公开动态数组。方法由helperclassTdwsRTTIExposer
单元dwsRTTIExposer
介绍。不幸的是,这只适用于一些基本类型的动态数组,而不适用于记录或对象。这是一个简单的 class,可帮助您在 TdwsUnit
实例的生命周期内注册记录类型和动态数组:
uses
System.SysUtils, System.Classes, System.Rtti, dwsComp, dwsExprs, dwsInfo,
dwsErrors, dwsRTTIExposer;
type
TDwsDynamicArrayExposer<T: record> = class(TComponent)
strict private
FRttiType: TRttiType;
FDwsSymbol: TdwsSymbol;
FDwsArray: TdwsArray;
function GetDwsUnit: TdwsUnit;
strict protected
class var RTTIContext: TRttiContext;
property DwsUnit: TdwsUnit read GetDwsUnit;
property RttiType: TRttiType read FRttiType;
property DwsSymbol: TdwsSymbol read FDwsSymbol;
property DwsArray: TdwsArray read FDwsArray;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure SetInfo(const Info: IInfo; const Values: TArray<T>);
end;
constructor TdwsDynamicArrayExposer<T>.Create(AOwner: TComponent);
begin
if not (AOwner is TdwsUnit) then
raise EArgumentException.Create('Owner must be instance of TdwsUnit.');
inherited;
FRttiType := RTTIContext.GetType(TypeInfo(T));
FDwsSymbol := DwsUnit.ExposeRTTI(FRttiType.Handle);
FDwsArray := DwsUnit.Arrays.Add;
FDwsArray.DataType := FDwsSymbol.Name;
FDwsArray.Name := FDwsSymbol.Name + 'Array';
FDwsArray.IsDynamic := True;
end;
destructor TdwsDynamicArrayExposer<T>.Destroy;
begin
if Assigned(DwsUnit) and (not (csDestroying in DwsUnit.ComponentState)) then
begin
// in case something went wrong in constructor
FDwsArray.Free;
FDwsSymbol.Free;
end;
inherited;
end;
function TdwsDynamicArrayExposer<T>.GetDwsUnit: TdwsUnit;
begin
Result := TdwsUnit(Owner);
end;
procedure TdwsDynamicArrayExposer<T>.SetInfo(const Info: IInfo; const Values: TArray<T>);
var
Index: Integer;
begin
Info.Member['Length'].ValueAsInteger := Length(Values);
for Index := 0 to Length(Values) - 1 do
TdwsRTTIInvoker.AssignRecordFromValue(Info.Element([Index]),
TValue.From<T>(Values[Index]), RttiType);
end;
class 还提供了方便的方法 SetInfo
用于从动态数组初始化 IInfo
实例(参数、变量、结果变量...)。
现在您可以为您的 TSampleRecord
定义专门的曝光器并在 DWS 单元中注册函数 GetSampleRecordArray
:
type
TSampleRecord = record
Name: string;
end;
TArrayOfSampleRecordExposer = class(TdwsDynamicArrayExposer<TSampleRecord>)
strict private
FGetSampleRecordArrayFunction: TdwsFunction;
procedure OnGetSampleRecordArrayEval(Info: TProgramInfo);
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
end;
function GetSampleRecordArray: TArray<TSampleRecord>;
begin
SetLength(Result, 3);
Result[0].Name := 'Name 0';
Result[1].Name := 'Name 1';
Result[2].Name := 'Name 2';
end;
constructor TArrayOfSampleRecordExposer.Create(AOwner: TComponent);
begin
inherited;
FGetSampleRecordArrayFunction := DwsUnit.Functions.Add;
FGetSampleRecordArrayFunction.Name := 'GetSampleRecordArray';
FGetSampleRecordArrayFunction.ResultType := DwsArray.Name;
FGetSampleRecordArrayFunction.OnEval := OnGetSampleRecordArrayEval;
end;
destructor TArrayOfSampleRecordExposer.Destroy;
begin
if Assigned(DwsUnit) and (not (csDestroying in DwsUnit.ComponentState)) then
FGetSampleRecordArrayFunction.Free;
inherited;
end;
procedure TArrayOfSampleRecordExposer.OnGetSampleRecordArrayEval(Info: TProgramInfo);
begin
SetInfo(Info.ResultVars, GetSampleRecordArray);
end;
最后你通过实例化TArrayOfSampleRecordExposer
注册了Delphi函数:
Dws := TDelphiWebScript.Create(nil);
DwsUnit := TdwsUnit.Create(Dws);
DwsUnit.UnitName := 'Unit1';
DwsUnit.Script := Dws;
// one-time registration
TArrayOfSampleRecordExposer.Create(DwsUnit);
// ...
DwsProgram := Dws.Compile(
'var SampleRecords := GetSampleRecordArray;'#13#10 +
'for var SampleRecord in SampleRecords do'#13#10 +
' Println(SampleRecord.Name);');
if DwsProgram.Msgs.Count > 0 then
raise Exception.Create(DwsProgram.Msgs.AsInfo);
DwsProgramExecution := DwsProgram.Execute;
这应该产生输出 (DwsProgramExecution.Result.ToString
):
Name 0
Name 1
Name 2
我已经在 DWScript 单元中声明了一个简单的记录类型:
TSampleRecord = record
name: string;
end;
如何将 Delphi 应用程序中的此类数组公开给脚本? 例如,Delphi 应用程序中的以下方法:
// Delphi side
function GetSampleRecordArray(): array of TSampleRecord;
必须可以从脚本访问:
// Script side
var myArray: array of TSampleRecord;
myArray := GetSampleRecordArray();
在 returns 动态记录数组的脚本中注册函数之前,您需要:
- 注册记录类型
- 注册该类型的动态数组
TdwsUnit
有辅助方法 ExposeRTTIDynamicArray
来为脚本公开动态数组。方法由helperclassTdwsRTTIExposer
单元dwsRTTIExposer
介绍。不幸的是,这只适用于一些基本类型的动态数组,而不适用于记录或对象。这是一个简单的 class,可帮助您在 TdwsUnit
实例的生命周期内注册记录类型和动态数组:
uses
System.SysUtils, System.Classes, System.Rtti, dwsComp, dwsExprs, dwsInfo,
dwsErrors, dwsRTTIExposer;
type
TDwsDynamicArrayExposer<T: record> = class(TComponent)
strict private
FRttiType: TRttiType;
FDwsSymbol: TdwsSymbol;
FDwsArray: TdwsArray;
function GetDwsUnit: TdwsUnit;
strict protected
class var RTTIContext: TRttiContext;
property DwsUnit: TdwsUnit read GetDwsUnit;
property RttiType: TRttiType read FRttiType;
property DwsSymbol: TdwsSymbol read FDwsSymbol;
property DwsArray: TdwsArray read FDwsArray;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure SetInfo(const Info: IInfo; const Values: TArray<T>);
end;
constructor TdwsDynamicArrayExposer<T>.Create(AOwner: TComponent);
begin
if not (AOwner is TdwsUnit) then
raise EArgumentException.Create('Owner must be instance of TdwsUnit.');
inherited;
FRttiType := RTTIContext.GetType(TypeInfo(T));
FDwsSymbol := DwsUnit.ExposeRTTI(FRttiType.Handle);
FDwsArray := DwsUnit.Arrays.Add;
FDwsArray.DataType := FDwsSymbol.Name;
FDwsArray.Name := FDwsSymbol.Name + 'Array';
FDwsArray.IsDynamic := True;
end;
destructor TdwsDynamicArrayExposer<T>.Destroy;
begin
if Assigned(DwsUnit) and (not (csDestroying in DwsUnit.ComponentState)) then
begin
// in case something went wrong in constructor
FDwsArray.Free;
FDwsSymbol.Free;
end;
inherited;
end;
function TdwsDynamicArrayExposer<T>.GetDwsUnit: TdwsUnit;
begin
Result := TdwsUnit(Owner);
end;
procedure TdwsDynamicArrayExposer<T>.SetInfo(const Info: IInfo; const Values: TArray<T>);
var
Index: Integer;
begin
Info.Member['Length'].ValueAsInteger := Length(Values);
for Index := 0 to Length(Values) - 1 do
TdwsRTTIInvoker.AssignRecordFromValue(Info.Element([Index]),
TValue.From<T>(Values[Index]), RttiType);
end;
class 还提供了方便的方法 SetInfo
用于从动态数组初始化 IInfo
实例(参数、变量、结果变量...)。
现在您可以为您的 TSampleRecord
定义专门的曝光器并在 DWS 单元中注册函数 GetSampleRecordArray
:
type
TSampleRecord = record
Name: string;
end;
TArrayOfSampleRecordExposer = class(TdwsDynamicArrayExposer<TSampleRecord>)
strict private
FGetSampleRecordArrayFunction: TdwsFunction;
procedure OnGetSampleRecordArrayEval(Info: TProgramInfo);
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
end;
function GetSampleRecordArray: TArray<TSampleRecord>;
begin
SetLength(Result, 3);
Result[0].Name := 'Name 0';
Result[1].Name := 'Name 1';
Result[2].Name := 'Name 2';
end;
constructor TArrayOfSampleRecordExposer.Create(AOwner: TComponent);
begin
inherited;
FGetSampleRecordArrayFunction := DwsUnit.Functions.Add;
FGetSampleRecordArrayFunction.Name := 'GetSampleRecordArray';
FGetSampleRecordArrayFunction.ResultType := DwsArray.Name;
FGetSampleRecordArrayFunction.OnEval := OnGetSampleRecordArrayEval;
end;
destructor TArrayOfSampleRecordExposer.Destroy;
begin
if Assigned(DwsUnit) and (not (csDestroying in DwsUnit.ComponentState)) then
FGetSampleRecordArrayFunction.Free;
inherited;
end;
procedure TArrayOfSampleRecordExposer.OnGetSampleRecordArrayEval(Info: TProgramInfo);
begin
SetInfo(Info.ResultVars, GetSampleRecordArray);
end;
最后你通过实例化TArrayOfSampleRecordExposer
注册了Delphi函数:
Dws := TDelphiWebScript.Create(nil);
DwsUnit := TdwsUnit.Create(Dws);
DwsUnit.UnitName := 'Unit1';
DwsUnit.Script := Dws;
// one-time registration
TArrayOfSampleRecordExposer.Create(DwsUnit);
// ...
DwsProgram := Dws.Compile(
'var SampleRecords := GetSampleRecordArray;'#13#10 +
'for var SampleRecord in SampleRecords do'#13#10 +
' Println(SampleRecord.Name);');
if DwsProgram.Msgs.Count > 0 then
raise Exception.Create(DwsProgram.Msgs.AsInfo);
DwsProgramExecution := DwsProgram.Execute;
这应该产生输出 (DwsProgramExecution.Result.ToString
):
Name 0
Name 1
Name 2