How can I get the sub-item type of a TObjectList purely by RTTI information (i.e. without using any actual object instance) in Delphi?

前端 未结 2 1140
一个人的身影
一个人的身影 2020-12-18 17:05

I\'m implementing generic code for streaming arbitrary Delphi objects using RTTI, and in order to get this to work (more specifically, in order to get the loading part to wo

2条回答
  •  难免孤独
    2020-12-18 18:06

    Unfortunately, there is no RTTI generated for Generic parameters. The only way to discover the value of T in a Generic container like TList is to get the TRttiType for the target field itself, call its ToString() method to get its class name as a string, and parse out the substring that is between the brackets. For example:

    uses
      ..., System.StrUtils, System.Rtti;
    
    var
      Ctx: TRttiContext;
      s: string;
      OpenBracket, CloseBracket: Integer;
      ...
    begin
      ...
      s := Ctx.GetType(TTestClass).GetField('test_list').FieldType.ToString; // returns 'TList'
      OpenBracket := Pos('<', s);
      CloseBracket := PosEx('>', s, OpenBracket+1);
      s := Copy(s, OpenBracket+1, CloseBracket-OpenBracket-1); // returns 'System.string'
      // if multiple Generic parameters are present, they will be separated by commas...
      ...
    end;
    

    Once you have extracted the Generic parameter as a string, you can use TRttiContext.FindType() if you need to access the RTTI for that type.

    With that said, the following code provides a bunch of RTTI helpers:

    DSharp.Core.Reflection.pas (Google Code)

    DSharp.Core.Reflection.pas (BitBucket)

    Amongst other things, it defines a TRttiTypeHelper class helper that adds a GetGenericArguments() method to TRttiType:

    TRttiTypeHelper = class helper for TRttiType
    ...
    public 
      ...
      function GetGenericArguments: TArray;
      ...
    end;
    

    Internally, GetGenericArguments() uses the same technique I mention above. With it, you can do this instead:

    uses
      ..., System.Rtti, DSharp.Core.Reflection;
    
    var
      Ctx: TRttiContext;
      arr: TArray;
      typ: TRttiType;
      s: string;
      ...
    begin
      ...
      arr := Ctx.GetType(TTestClass).GetField('test_list').FieldType.GetGenericArguments;
      typ := arr[0]; // returns RTTI for System.String
      s := typ.ToString; // returns 'System.string'
      ...
    end;
    

提交回复
热议问题