Cast member of generic type to TObject?
I am currently thinking about a scenario where I want a generic class that calls Free
on its items IF these are of an object type. So I tried the following:
if (PTypeInfo (TypeInfo (T)).Kind = tkClass) then
begin
RttiType := RttiContext.GetType (TypeInfo (T));
FreeMethod := RttiType.GetMethod ('Free');
if (Fre开发者_如何学PythoneMethod <> nil) then
FreeMethod.Invoke (FInstance, []);
end;
Unfortunately the last line does not compile, which is not surprising since the types do not match. The question is: can I get it to compile? I tried casting to Pointer
and then to TObject
but that gives me an invalid typecast error. Is there any way to get FInstance : T
into a TObject
reference that I can then pass to Invoke
?
Or is there any other way to achieve what I want? Please note that the whole point is to get everything in this class, so I do not want to create a TObjectMyClass
that just takes objects.
Thanks for your help.
Isn't this sufficient?
if PTypeInfo(TypeInfo(T))^.Kind = tkClass then
TObject(FInstance).Free;
Direct cast to TObject seems to work in Delphi 2010:
FreeMethod.Invoke (TObject(FInstance), []);
Full example:
implementation
uses TypInfo, Rtti;
{$R *.dfm}
type
TTest<T> = class
FInstance : T;
procedure F;
end;
procedure TForm1.FormCreate(Sender: TObject);
var
O : TTest<TObject>;
begin
O := TTest<TObject>.Create;
O.FInstance := TStringList.Create;
O.F;
O.Free;
end;
{ TTest<T> }
procedure TTest<T>.F;
var
RttiType : TRttiType;
FreeMethod : TRttiMethod;
RttiContext : TRttiContext;
begin
if (PTypeInfo (TypeInfo (T)).Kind = tkClass) then
begin
RttiType := RttiContext.GetType (TypeInfo (T));
FreeMethod := RttiType.GetMethod ('Free');
if (FreeMethod <> nil) then
FreeMethod.Invoke (TObject(FInstance), []);
end;
end;
This smacks of poor design to me. One of the points of generics is to get away from dynamic type decisions like this.
If you really are using the generics feature then it would make more sense to me to have a subclass with a type parameter that is constrained take only TObject
descendents. Since you have to decide statically how you are going to instantiate the generic class, it is no trouble whatsoever to use a TMyObjectClass
when T
is a class and to use TMyClass
elsewhere.
I guess you'd be forced to take the approach you are taking if a single container needs to contain both objects and non-objects. But if that's not the case then something just feels off to me.
Actually, I just found the answer myself. TValue.From
does the trick:
if (PTypeInfo (TypeInfo (T)).Kind = tkClass) then
begin
RttiType := RttiContext.GetType (TypeInfo (T));
FreeMethod := RttiType.GetMethod ('Free');
if (FreeMethod <> nil) then
FreeMethod.Invoke (TValue.From <T> (FInstance), []);
end;
精彩评论