Delphi 如何为枚举RTTI字段创建通用TValue?

Delphi 如何为枚举RTTI字段创建通用TValue?,delphi,delphi-xe,rtti,Delphi,Delphi Xe,Rtti,问题中显示了创建与SetValue一起使用的兼容TValue的方法。我正在尝试制作一个通用版本,使用RTTI将类存储到INI文件中。这是我的简化代码: procedure TMyClass.LoadRTTI(xObject: TObject); var LContext: TRttiContext; LClass: TRttiInstanceType; xField : TRttiField; szNewValue : String; xValue : TValue; beg

问题中显示了创建与SetValue一起使用的兼容TValue的方法。我正在尝试制作一个通用版本,使用RTTI将类存储到INI文件中。这是我的简化代码:

procedure TMyClass.LoadRTTI(xObject: TObject);
var
  LContext: TRttiContext;
  LClass: TRttiInstanceType;
  xField : TRttiField;
  szNewValue : String;
  xValue : TValue;
begin
  LContext := TRttiContext.Create;
  LClass := LContext.GetType(xObject.ClassType) as TRttiInstanceType;

  for xField in LClass.GetDeclaredFields do
  begin
    szNewValue := IniFile.ReadString(szSection, xField.Name, '');
    if szNewValue <> '' then // emumerated will be '0' (zero) as that is what GetValue.AsString returns
    begin
      case xField.FieldType.TypeKind of
      tkEnumeration: xValue := StrToIntDef(szNewValue, xField.GetValue(xObject).AsOrdinal);
      end;
      xField.SetValue(xObject, xValue); // FAILS HERE with 'Invalid calss typecast
    end;
  end;
end;
程序TMyClass.LoadRTTI(xObject:TObject);
变量
LContext:trttitcontext;
l类:trttInstanceType;
xField:TRttiField;
szNewValue:String;
xValue:TValue;
开始
LContext:=TRttiContext.Create;
LClass:=LContext.GetType(xObject.ClassType)作为TRttiInstanceType;
对于LClass.GetDeclaredFields中的xField
开始
szNewValue:=IniFile.ReadString(szSection,xField.Name“”);
如果szNewValue为“”,则//emumerated将为“0”(零),因为这是GetValue.AsString返回的值
开始
案例xField.FieldType.TypeKind of
tkEnumeration:xValue:=strotintdef(szNewValue,xField.GetValue(xObject.AsOrdinal));
结束;
xField.SetValue(xObject,xValue);//此处失败,出现“无效calss typecast”
结束;
结束;
结束;
在引用的答案中,解决方案是使用TValue.From()方法获取值,但这似乎需要适当类型的变量。我没有这样的类型,因为我的代码不知道它是什么


我正在寻找一个通用方法的示例,从RTTI获取字符串中的值,然后再将其放回去。我还没有找到一个好的教程来介绍这一点。

在赋值之前,必须先设置
TValue
的实例,然后使用
GetEnumValue
函数将字符串转换为枚举值

请尝试以下代码:

procedure TMyClass.LoadRTTI(xObject: TObject);
var
  LContext: TRttiContext;
  LClass: TRttiInstanceType;
  xField : TRttiField;
  szNewValue : String;
  xValue : TValue;
begin
  LContext := TRttiContext.Create;
  LClass := LContext.GetType(xObject.ClassType) as TRttiInstanceType;

  for xField in LClass.GetDeclaredFields do
  begin
    szNewValue := IniFile.ReadString(szSection, xField.Name, '');
    if szNewValue <> '' then // emumerated will be '0' (zero) as that is what GetValue.AsString returns
    begin
      case xField.FieldType.TypeKind of
      tkEnumeration: 
                   begin
                     //get the instance to the TValue to set
                     xValue:=xField.GetValue(xObject);
                     //convert the data to a valid TValue
                     xValue:=TValue.FromOrdinal(xValue.TypeInfo,GetEnumValue(xValue.TypeInfo,szNewValue));
                   end;

      end;
      //assign the new value from the TValue
      xField.SetValue(xObject, xValue); 
    end;
  end;
end;
程序TMyClass.LoadRTTI(xObject:TObject);
变量
LContext:trttitcontext;
l类:trttInstanceType;
xField:TRttiField;
szNewValue:String;
xValue:TValue;
开始
LContext:=TRttiContext.Create;
LClass:=LContext.GetType(xObject.ClassType)作为TRttiInstanceType;
对于LClass.GetDeclaredFields中的xField
开始
szNewValue:=IniFile.ReadString(szSection,xField.Name“”);
如果szNewValue为“”,则//emumerated将为“0”(零),因为这是GetValue.AsString返回的值
开始
案例xField.FieldType.TypeKind of
tk消耗:
开始
//获取要设置的TValue的实例
xValue:=xField.GetValue(xObject);
//将数据转换为有效的TValue
xValue:=TValue.FromOrdinal(xValue.TypeInfo,GetEnumValue(xValue.TypeInfo,szNewValue));
结束;
结束;
//从TValue中指定新值
xField.SetValue(xObject,xValue);
结束;
结束;
结束;

以下是一些示例代码,展示了如何做到这一点:

var
  V : TValue;
  OrdValue : Integer;
  C : TRttiContext;
  F : TRttiField;
  lTypeInfo : PTypeInfo;
begin

  // Pick a Enumerated Field
  F := C.GetType(TForm).GetField('FFormStyle');

  // Get the TypeInfo for that field
  lTypeInfo := F.FieldType.Handle;

  // Setting TValue from an Enumeration Directly.
  V := TValue.From(FormStyle);
  ShowMessage(V.ToString);
  // Setting TValue from the ordinal value of a Enumeration
  OrdValue := ord(FormStyle);
  V := TValue.FromOrdinal(lTypeInfo,OrdValue);
  ShowMessage(V.ToString);
  // Setting TValue from the String Value of an enumeration.
  OrdValue := GetEnumValue(lTypeInfo,'fsStayOnTop');
  V := TValue.FromOrdinal(lTypeInfo,OrdValue);
  ShowMessage(V.ToString);
end;

我也有同样的问题,但我用另一种方法解决了。更快的方法:

type
  CustType = (ctNone, ctEverything, ctNothing);

  TObjctCust = class(TObject)
    InfoType: CustType;
  end;

procedure TForm34.Button1Click(Sender: TObject);
var
  CurContext: TRttiContext;
  Test: TObjctCust;
  CurClassType: TRttiType;
  CurFields: TArray<TRttiField>;
  I: Integer;
  Field: TRttiField;
  TypeValue: Integer;
  LFieldPointer: Pointer;
  TypedSmallInt: SmallInt;
begin
  Test := TObjctCust.Create;

  CurContext := TRttiContext.Create;
  CurClassType := CurContext.GetType(Test.ClassType);
  CurFields := CurClassType.GetFields;

  //Here you can set any integer value you'd like to set in the type field. For example the result of query (AsInteger, AsOrdinal)
  TypeValue := 1;
  for I := 0 to Length(CurFields) -1 do
  begin
    Field := CurFields[I];
    if Field.FieldType.TypeKind = tkEnumeration then
    begin
      //Here is the solution, I change the value direct in the field position
      LFieldPointer := Pointer(PByte(Test) + Field.Offset);
      TypedSmallInt := TypeValue;
      Move(TypedSmallInt, LFieldPointer^, Field.FieldType.TypeSize);
    end;
  end;

  ShowMessage(IntToStr(Ord(Test.InfoType)));
end;
类型
CustType=(ctNone、ctEverything、ctNothing);
TObjctCust=class(TObject)
信息类型:CustType;
结束;
程序TForm34.按钮1单击(发件人:ToObject);
变量
CurContext:TRttiContext;
测试:TObjctCust;
CurClassType:trtttype;
柯菲:柏油;
I:整数;
字段:TRttiField;
TypeValue:整数;
LFieldPointer:指针;
typedsmalint:SmallInt;
开始
Test:=TObjctCust.Create;
CurContext:=TRttiContext.Create;
CurClassType:=CurContext.GetType(Test.ClassType);
CurFields:=CurClassType.GetFields;
//您可以在这里设置要在类型字段中设置的任何整数值。例如查询结果(AsInteger、AsOrdinal)
类型值:=1;
对于I:=0到长度(CurFields)-1 do
开始
字段:=CurFields[I];
如果Field.FieldType.TypeKind=tkEnumeration,则
开始
//这是解决方案,我直接在字段位置更改值
LFieldPointer:=指针(字节(测试)+字段偏移量);
TypedSalint:=类型值;
移动(typedsmalint,LFieldPointer^,Field.FieldType.TypeSize);
结束;
结束;
ShowMessage(IntToStr(Ord(Test.InfoType));
结束;

好吧,一旦我不再聪明地使用我的保存代码,这就很有效了。要获得保存到INI的值,只需使用xField.GetValue(xObject.ToString;谢谢你-这是一套很好的选项和备选方案,可以让这个问题更好地记录在其他情况下如何做到这一点。