我必须翻译一些Fortran 90代码并找到一个有趣的语言功能.
例如,它们定义以下类型和动态数组变量:
TYPE WallInfo CHARACTER(len=40) :: Name REAL :: Azimuth REAL :: Tilt REAL :: Area REAL :: Height END TYPE WallInfo TYPE(WallInfo),ALLOCATABLE,DIMENSION(:) :: Wall
CALL HeatFlow(Wall%Area,Wall%Azimuth)
作为一名Delphi程序员,这让我感到有些不同,因为Wall是一系列记录!
从例程中的用法来看,很明显Fortran可以将记录数组中的字段作为自己的数组进行投影.
SUBROUTINE HeatFlow( Area,Azimuth ) REAL,INTENT(IN),DIMENSION(:) :: Area REAL,DIMENSION(:) :: Azimuth
有没有人知道是否有办法用Delphi(我使用的是2010版)?
我可以编写一个函数来将记录值提取为数组,但这有点单调乏味,因为我必须为每个字段编写一个专用例程(并且有很多).
我希望Delphi 2010中有一些我错过的语言功能.
解决方法
使用扩展RTTI,可以创建一个通用函数,该函数将数组和字段名称作为输入,并使用数组的RTTI仅提取该字段的值,并使用正确的数据类型创建具有它们的新数组.
以下代码适用于XE2:
uses System.SysUtils,System.Rtti; type FieldArray<TArrElemType,TFieldType> = class public class function Extract(const Arr: TArray<TArrElemType>; const FieldName: String): TArray<TFieldType>; end; class function FieldArray<TArrElemType,TFieldType>.Extract(const Arr: TArray<TArrElemType>; const FieldName: String): TArray<TFieldType>; var Ctx: TRttiContext; LArrElemType: TRttiType; LField: TRttiField; LFieldType: TRttiType; I: Integer; begin Ctx := TRttiContext.Create; try LArrElemType := Ctx.GetType(TypeInfo(TArrElemType)); LField := LArrElemType.GetField(FieldName); LFieldType := Ctx.GetType(TypeInfo(TFieldType)); if LField.FieldType <> LFieldType then raise Exception.Create('Type mismatch'); SetLength(Result,Length(Arr)); for I := 0 to Length(Arr)-1 do begin Result[I] := LField.GetValue(@Arr[I]).AsType<TFieldType>; end; finally Ctx.Free; end; end;
.
type WallInfo = record Name: array[0..39] of Char; Azimuth: Real; Tilt: Real; Area: Real; Height: Real; end; procedure HeatFlow(const Area: TArray<Real>; const Azimuth: TArray<Real>); begin // Area contains (4,9) an Azimuth contains (2,7) as expected ... end; var Wall: TArray<WallInfo>; begin SetLength(Wall,2); Wall[0].Name := '1'; Wall[0].Azimuth := 2; Wall[0].Tilt := 3; Wall[0].Area := 4; Wall[0].Height := 5; Wall[1].Name := '6'; Wall[1].Azimuth := 7; Wall[1].Tilt := 8; Wall[1].Area := 9; Wall[1].Height := 10; HeatFlow( FieldArray<WallInfo,Real>.Extract(Wall,'Area'),FieldArray<WallInfo,'Azimuth') ); end;