我正在使用Delphi Seattle Update1 Win64并尝试使用RTTI提取属性。我的目标是将组件属性序列化为JSON,因为我需要在非Delphi环境中使用此信息。
我的问题是关于GetPropList
(示例)的TRectangle
以及为什么它会返回无法传递给GetPropValue
的属性,即:
StrokeThickness
类型tkFloat
StrokeCap
类型tkEnumeration
StrokeDash
类型tkEnumeration
StrokeJoin
作为类型tkEnumeration。 GetPropList
确实正确返回Stroke
作为tkClass
类型,这是我所期望的,并且在解析时,Stroke类返回Thickness
,Cap
,Dash
和Join
,我可以从这些中获取正确的值。
问题是在GetPropValue
上执行StrokeThickness
会导致异常。因此,我必须特别注意GetPropList
返回的“损坏”属性,我希望避免这种情况。
起初我认为这是一个问题,GetPropList返回一个不存在的属性,但我可以在代码中执行以下命令,它们都有效:
Rectangle1.StrokeThickness := 5; //works
Rectangle1.Stroke.Thickness := 10; //and also works
tkFloat或tkEnumeration类型的其他属性按预期工作并返回正确的值。
我创建了一个小测试应用程序来尝试调试它。我发现在StrokeThickness的情况下,M.Code在函数System.TypeInfo.TPropSet.GetProp(第2397行)中为零,我想这解释了为什么会导致异常。
附件是我创建的测试代码,用于确认我在更大的项目中看到的内容。如何在没有特殊情况下处理上面列出的四个属性。
表格:
object Form1: TForm1
Left = 0
Top = 0
Caption = 'Form1'
ClientHeight = 202
ClientWidth = 542
FormFactor.Width = 320
FormFactor.Height = 480
FormFactor.Devices = [Desktop]
DesignerMasterStyle = 0
object Rectangle1: TRectangle
Position.X = 40.000000000000000000
Position.Y = 40.000000000000000000
Size.Width = 97.000000000000000000
Size.Height = 97.000000000000000000
Size.PlatformDefault = False
end
object StrokeThickness: TButton
Position.X = 40.000000000000000000
Position.Y = 144.000000000000000000
Size.Width = 97.000000000000000000
Size.Height = 22.000000000000000000
Size.PlatformDefault = False
TabOrder = 1
Text = 'RTTI'
OnClick = StrokeThicknessClick
end
object Memo1: TMemo
Touch.InteractiveGestures = [Pan, LongTap, DoubleTap]
DataDetectorTypes = []
Position.X = 152.000000000000000000
Position.Y = 40.000000000000000000
Size.Width = 353.000000000000000000
Size.Height = 129.000000000000000000
Size.PlatformDefault = False
TabOrder = 2
Viewport.Width = 349.000000000000000000
Viewport.Height = 125.000000000000000000
end
end
测试代码:
unit Unit1;
interface
uses
System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.StdCtrls,
FMX.Controls.Presentation, FMX.Edit, FMX.Objects, FMX.ScrollBox, FMX.Memo;
type
TForm1 = class(TForm)
Rectangle1: TRectangle;
StrokeThickness: TButton;
Memo1: TMemo;
procedure StrokeThicknessClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
uses System.TypInfo;
{$R *.fmx}
procedure TForm1.StrokeThicknessClick(Sender: TObject);
var
vValue : String;
PropList : PPropList;
PropInfo : PPropInfo;
PropType : PPTypeInfo;
PropListCount : Integer;
I: Integer;
begin
memo1.Lines.Clear;
PropListCount := GetPropList(Rectangle1, PropList);
for I := 0 to PropListCount-1 do
begin
PropInfo := PropList^[I];
PropType := PropInfo^.PropType;
Memo1.Lines.Add('Name: '+String(PropInfo^.Name) );
Memo1.Lines.Add('PropType: '+String(PropInfo^.PropType^.Name) );
Memo1.Lines.Add('PropKind: '+GetEnumName(TypeInfo(TTypeKind), Ord(PropType^.Kind)) );
Memo1.Lines.Add('');
end;
vValue := GetPropValue(Rectangle1, 'Name'); //test string
Memo1.Lines.Add('Proprty Name = '+VarToStr(vValue) );
vValue := GetPropValue(Rectangle1, 'Height'); //test float
Memo1.Lines.Add('Property Height = '+VarToStr(vValue) );
vValue := GetPropValue(Rectangle1, 'Sides'); //test enumeration
Memo1.Lines.Add('Property Sides = '+VarToStr(vValue) );
//The following would cause an exception
{
vValue := GetPropValue(Rectangle1, 'StrokeThickness');
Memo1.Lines.Add('Property StrokeThickness ='+VarToStr(vValue));
}
Rectangle1.StrokeThickness := 5; //works ??
//Still fails after it was explicitly set
{
vValue := GetPropValue(Rectangle1, 'StrokeThickness');
Memo1.Lines.Add('Property StrokeThickness ='+VarToStr(vValue));
}
Rectangle1.Stroke.Thickness := 10; //and also works... as expected
//The following with cause an exception
{
vValue := GetPropValue(Rectangle1, 'StrokeDash');
Memo1.Lines.Add('StrokeDash = '+VarToStr(vValue) );
}
end;
end.
答案 0 :(得分:1)
使用像
这样的代码var
LProperty: TRttiProperty;
LType: TRttiType;
LContext: TRttiContext;
LArray: TArray<TRttiProperty>;
begin
LContext := TRTTIContext.Create;
LType := LContext.GetType(TRectangle);
LArray := LType.GetDeclaredProperties;
for LProperty in LArray do
begin
Memo1.Lines.Add('Name: ' + LProperty.Name);
Memo1.Lines.Add('PropType: ' + LProperty.PropertyType.Name);
Memo1.Lines.Add('PropKind: ' + GetEnumName(TypeInfo(TTypeKind), Ord(LProperty.PropertyType.TypeKind)));
if LProperty.IsReadable then
begin
Memo1.Lines.Add('Value: ' + LProperty.GetValue(Rectangle1).ToString);
end
else
begin
Memo1.Lines.Add('Value of the property cannot be read');
end;
Memo1.Lines.Add('');
end;
end;