2010-05-05 5 views
3

생각이 있으십니까? 원래 데이터에 대한 참조를 사용하여 TValue를 만드는 방법은 무엇입니까? 내 직렬화 프로젝트에서 (예 : MemberMap과 비슷한) 내부 트리 구조에 TValue를 저장하는 일반 직렬 변환기를 (XML-Serialization에서 제안 된대로) 사용합니다.Delphi 2010에서의 Rtti 데이터 조작 및 일관성

이 구성원 트리를 사용하여 동적 설치 양식을 만들고 데이터를 조작해야합니다. 내 생각은 데이터에 대한 속성 정의했다 :

TDataModel <T> = class 
    {...} 
    private 
    FData : TValue; 
    function GetData : T; 
    procedure SetData (Value : T); 
    public 
    property Data : T read GetData write SetData; 
end; 

GetData의, SetData를 메소드의 구현은 :

procedure TDataModel <T>.SetData (Value : T); 

begin 
FData := TValue.From <T> (Value); 
end; 

procedure TDataModel <T>.GetData : T; 

begin 
Result := FData.AsType <T>; 
end; 

불행하게도, TValue.From 방법은 항상 원본 데이터의 복사본을 만듭니다를 . 따라서 응용 프로그램에서 데이터를 변경하면 DataModel은 업데이트되지 않으며 DataModel을 동적 폼으로 변경하면 원래 데이터는 영향을받지 않습니다. 물론 언제든지 Data 속성을 변경하기 전후에 사용할 수 있지만 DataModel 내부에서 많은 Rtti를 사용하기 때문에 언제든지이 작업을 수행하고 싶지는 않습니다.

아마도 누군가가 더 좋은 제안을 갖고 있습니까?

답변

4

TValue는 모든 종류의 데이터를 매우 작게 유지하도록 설계되었으며 "포인터"를 에뮬레이트하도록 설계되지 않았습니다. RTTI.pas 유닛을 살펴보십시오. TValue는 TValueData 유형의 데이터 멤버 하나만 가진 레코드입니다. TValueData 자체는 자체 변형 레코드입니다.

TValueData를 보면 최소 데이터 양 이외의 정보가 저장되지 않는 것을 확인할 수 있습니다. TV가 어디서 왔는지 알 수있는 방법이 없습니다.

해결책 : 구조에 TValue를 두지 말고 한 쌍의 TRttiField + TObject로 바꾸십시오. TValue에서 TRttiField.GetValue (인스턴스)를 사용해야하는 경우 값을 설정하려면 TRttiField.SetValue (인스턴스, AValue : TValue)를 사용합니다.

+0

이것은 첫 번째 방법이었고 당신이 옳습니다 : 이것은 객체들에게 적용됩니다. 하지만 T가 레코드이고 TObject가 아닌 경우는 어떻게 될까요? –

+1

레코드 인 경우 일반 포인터가 있다고 가정합니다. TRttiField.GetValue 및 TRttiField.SetValue는 TObject가 아닌 Pointer 유형의 Instance 매개 변수를 사용합니다. 레코드 용 RTTI를 사용하지 않았고 절대로 필요하지 않았습니다. Delphi 2010의 RTTI는 레코드와 클래스 모두를 훌륭하게 처리하도록 설계되었습니다! TRttiField 대신 RTTI에서 얻은 객체 (TRttiProperty?)를 사용하고 TObject 대신 TRtti 객체에 필요한 객체를 사용하십시오. 처음에는 TValue를 얻는 방법을 찾았으므로 필요한 모든 것을 이미 갖추고 있습니다. –

0

도움을 주신 Cosmin에게 감사드립니다. 해결 방법은 구조에 TValue를 저장하지 말고 데이터에 대한 포인터를 사용하고 필드 또는 속성의 GetValue, SetValue 메소드를 사용하는 것입니다. 그래서 여기

내가 내 제네릭 클래스에서 그것을 해결하는 방법입니다

TDataModel <T> = class 
    private 
    FType  : PTypeInfo; 
    FInstance : Pointer; 
    public 
    constructor Create (var Data : T); 
    procedure ShowContent; 
    end; 

constructor TDataModel <T>.Create (var Data : T); 

begin 
FType := TypeInfo(T); 
if FType.Kind = tkClass then 
    FInstance := TObject (Data) 
else if FType.Kind = tkRecord then 
    FInstance := @Data; 
end; 

procedure TDataModel <T>.ShowContent; 
var 
    Ctx : TRttiContext; 
    Field : TRttiField; 

begin 
for Field in Ctx.GetType (FType).GetFields do 
    Writeln (Field.GetValue (FInstance).ToString); 
end; 

이제 할 수 있습니다 통해 DataModel이를 사용하거나 원래의 기록 클래스를 사용하거나 변경 필드.