我想在运行时使用D2010填充通用对象的字段。
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();
行时,我在System.pas(函数_IntfClear())的第21986行得到一个AV。这是从rtti.pas中的FContextToken := nil
调用的。 (事实上,如果我进入SetValue
,SetValue
引起的AV会弹出,但如果跳过它,则只会报告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仍然消失。)
我现在很伤心。
为了完整起见,我希望如何使用上述代码:
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.
有谁知道我做错了什么? (这是否可能?使用泛型有更好的方法吗?)
答案 0 :(得分:5)
好吧,我不知道这对你们是否有意义,但这就是我解决它的方法。在procedure TGeneric<TElement>.FillFields
中强硬转换为TObject就像一个魅力。像这样:
ctx.GetType(TypeInfo(TElement)).GetField('FField1').
SetValue(TObject(Element), TValue.FromVariant('Field 1 is set'));
希望这对其他人有用。