2011-08-24 67 views
5

here这个问题中,显示了一个用于创建与SetValue一起使用的兼容TValue的方法。我试图做出这个的通用版本,使用RTTI将类存储到INI文件中。这是我的削减代码:如何为枚举的RTTI字段创建一个通用TValue?

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; 

中引用了答案,该解决方案是让使用TValue.From()方法的价值,但似乎需要适当类型的变量。我没有这样的类型,因为我的代码不知道它是什么。

我正在寻找一个通用的方法来从RTTI获取字符串中的值的示例,并在以后再次放回。我还没有找到一个很好的教程,涵盖了这一点。

回答

7

你必须得到一个实例的TValue assing值之前设置,然后使用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; 
+0

好的,一旦我停止在我的保存代码中的聪明,这工作得很好。要获取保存到INI的值,只需使用xField.GetValue(xObject).ToString; – mj2008

6

下面是一些示例代码显示如何做到这一点:

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; 
+0

谢谢你 - 这是一个很好的选择和替代方案,可以让问题更好地记录如何在其他情况下做到这一点。 – mj2008

0

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

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;