开发者

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;
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜