2011-08-24 4 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에서 문자열의 값을 가져 와서 다시 사용하는 일반적인 방법을 찾고 있습니다. 나는 이것을 아직 다루고있는 훌륭한 튜토리얼을 발견하지 못했다.

다음
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; 

답변

7

당신은

이 코드를 사용해보십시오 값을 assing 전에 설정 한 다음 GetEnumValue 기능을 사용하여 열거 된 값으로 문자열을 변환 할 TValue에 인스턴스를 받아야합니다 예제 코드는 다음과 같이 보여줍니다 :

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

좋아, 저의 저축 코드에서 영리하지 않게되면 좋았습니다. 값을 INI에 저장하려면 xField.GetValue (xObject) .ToString;을 사용하십시오. – mj2008

6

일부입니다 :

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