2011-11-10 60 views
2

我想在運行時使用D2010填充通用對象的字段。Delphi:如何使用RTTI設置泛型的字段值?

program generic_rtti_1; 
{$APPTYPE CONSOLE} 
uses 
    SysUtils, rtti; 
type 
    TMyObject = class 
    FField1: string; 
    end; 
    TGeneric<TElement: class> = class 
    procedure FillFields(Element: TElement); 
    end; 
procedure TGeneric<TElement>.FillFields(Element: TElement); 
var 
    ctx: TRttiContext; 
begin 
    ctx := TRttiContext.Create(); 
    ctx.GetType(TypeInfo(TElement)).GetField('FField1'). 
    SetValue(@Element, TValue.FromVariant('Some string')); 
    ctx.Free(); 
end; 

當執行線ctx.Free();,我得到在線路21986在System.pas的AV(函數_IntfClear())。這是從rtti.pas中的FContextToken := nil中調用的。 (事實上​​,SetValue誘導AV彈出,如果我踏入SetValue,但是如果步過它,只有ctx.Free誘導的報道。見下面)。

如果我刪除ctx.Free();,呼籲SetValue(@Element, TValue.FromVariant('Some string'));當出現AV 。這也在System.pas的第21986行。

試圖弄清楚這個爛攤子了,我換成

ctx.GetType(TypeInfo(TElement)).GetField('FField1'). 
    SetValue(@Element, TValue.FromVariant('Field 1 is set')); 

與此:

rType := ctx.GetType(TypeInfo(TElement)); 
rField := rType.GetField('FField1'); 
Val := TValue.FromVariant('Field 1 is set'); 
rField.SetValue(@Element, Val); 

這一次,我沒有錯誤,但WriteLn(MyObject.FField1)打印一個空字符串。 (如果我結合SetValueTValue.FromVariant,即AV再次出現寫rField.SetValue(@Element, TValue.FromVariant('Field 1 is set'));

爲了查明犯行,我註釋掉一行行,用複合語句代替註釋的代碼。無意間,我忘了註釋掉Val := TValue.FromVariant('Field 1 is set');直插以上,這將導致AV消失一次(仍然叫rField.SetValue(@Element, TValue.FromVariant('Field 1 is set'));)。(請注意,我實際上並不使用Val的麻煩呼叫,仍是AV消失。)

我'kind'a kind'a lost at this point。

爲了完整起見,這裏就是我想怎麼用上面的代碼:

var 
    Generic: TGeneric<TMyObject>; 
    MyObject: TMyObject; 
begin 
    MyObject := TMyObject.Create(); 
    Generic := TGeneric<TMyObject>.Create(); 
    Generic.FillFields(); 
    WriteLn(MyObject.FField1); 
    Generic.Free(); 
    MyObject.Free(); 
    ReadLn; 
end; 
end. 

難道誰知道我做錯了嗎? (這是甚至可能的嗎?有沒有更好的方法來使用泛型來做到這一點?)

回答

5

嗯,我不知道這對你們是否有意義,但這是我如何解決它。硬投向TObject procedure TGeneric<TElement>.FillFields就像一個魅力。像這樣:

ctx.GetType(TypeInfo(TElement)).GetField('FField1'). 
    SetValue(TObject(Element), TValue.FromVariant('Field 1 is set')); 

希望這對其他人有用。

+0

當然它是有道理的,因爲SetValue需要一個指針,而一個對象是一個指針。我認爲,只要你的泛型類型受到了類限制,你甚至可以不使用hardcast。 –

+0

@StefanGlienke嗯,我在第一次去的時候傳入了'Element'的地址。同時,約束始終是「班級」。編譯器沒有找到它,所以我不得不開始嘗試。現在,約束'class'基本上意味着'TObject' - 對嗎?如果您試圖將泛型限制爲「TObject」,那麼您會很好地被告知「TObject不是有效的約束」。所以這似乎是最小的解決方案。 – conciliator