2011-08-24 9 views
5

W pytaniu here wyświetlana jest metoda tworzenia kompatybilnej TValue do użycia z SetValue. Próbuję zrobić ogólną wersję tego, aby użyć RTTI do przechowywania klasy w pliku INI. To jest mój wyciąć kod:Jak utworzyć ogólny telewizor dla wyliczonego pola 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: xValue := StrToIntDef(szNewValue, xField.GetValue(xObject).AsOrdinal); 
     end; 
     xField.SetValue(xObject, xValue); // FAILS HERE with 'Invalid calss typecast 
    end; 
    end; 
end; 

odpowiedź odwołanie, rozwiązanie było uzyskać wartość przy użyciu metody TValue.From(), ale wydaje się, że wymagają zmienną odpowiedniego typu. Nie mam takiego typu, ponieważ mój kod nie wie, co to jest.

Poszukuję przykładu ogólnego sposobu uzyskania wartości w ciągu znaków z RTTI, a następnie ponownie go wstawię. Nie znalazłem jeszcze dobrego samouczka, który by to opisał.

Odpowiedz

7

Musisz się wystąpienie do TValue ustawić przed assing wartość, a następnie przekonwertować ciąg wartości wyliczenia przy użyciu funkcji GetEnumValue

Spróbuj kod:

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

OK, zadziałało dobrze, gdy przestałem być sprytny w moim zbawczym kodzie. Aby uzyskać wartość do zapisania w INI, wystarczy użyć xField.GetValue (xObject) .ToString; – mj2008

6

Oto przykładowy kod pokazujący, jak to zrobić:

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

Dziękuję za to - jest to dobry zestaw opcji i alternatyw, które sprawią, że pytanie będzie lepszym zapisem tego, jak to zrobić w innych sytuacjach. – mj2008

0

Miałem ten sam problem, ale rozwiązałem go w inny sposób. Szybszy sposób:

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; 
Powiązane problemy