我想在運行時使用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)
打印一個空字符串。 (如果我結合SetValue
和TValue.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.
難道誰知道我做錯了嗎? (這是甚至可能的嗎?有沒有更好的方法來使用泛型來做到這一點?)
當然它是有道理的,因爲SetValue需要一個指針,而一個對象是一個指針。我認爲,只要你的泛型類型受到了類限制,你甚至可以不使用hardcast。 –
@StefanGlienke嗯,我在第一次去的時候傳入了'Element'的地址。同時,約束始終是「班級」。編譯器沒有找到它,所以我不得不開始嘗試。現在,約束'class'基本上意味着'TObject' - 對嗎?如果您試圖將泛型限制爲「TObject」,那麼您會很好地被告知「TObject不是有效的約束」。所以這似乎是最小的解決方案。 – conciliator