unit TypInfo_Delphi;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, TypInfo, StdCtrls, Generics.Collections;

type
  TForm2 = class(TForm)
    Memo1: TMemo;
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;
{ Defining a generic type that contains a single class (static)
  method that will print information about the type it operates on.
}
  TGenericClass<T> = class(TList<T>)
  public
    procedure PrintTypeInfo(memo: TMemo);
  end;
  TGenericString = class(TGenericClass<UnicodeString>)
  end;
  TGenericByte = class(TGenericClass<Byte>)
  end;
  TGenericDouble = class(TGenericClass<Double>)
  end;

var
  Form2: TForm2;

implementation

{$R *.dfm}

{ Method defintition }
procedure TGenericClass<T>.PrintTypeInfo(memo: TMemo);
var
  Info     : PTypeInfo;
  Data     : PTypeData;
  KindName : String;
  SubName  : String;
begin
  { Get type info for the "yet unknown type" }
  Info := System.TypeInfo(T);

  { There is no RTTI attached for some types like Records for example. }
  if Info <> nil then
  begin
    memo.Lines.Add('Type name: ' + Info^.Name);

    { Find out the name of an enum item from it's ordinal value }
    KindName := TypInfo.GetEnumName(System.TypeInfo(TTypeKind), Ord(Info^.Kind));

    memo.Lines.Add('Type kind: ' + KindName);

    Data := GetTypeData(Info);

    if Info^.Kind = tkInteger then
    begin
      { In case of integer let's see the actual sub-type name }
      SubName := TypInfo.GetEnumName(System.TypeInfo(TOrdType), Ord(Data^.OrdType));
      memo.Lines.Add('Integer kind: ' + SubName);
    end;

    if Info^.Kind = tkFloat then
    begin
      { In case of float let's see the actual sub-type name }
      SubName := TypInfo.GetEnumName(System.TypeInfo(TFloatType), Ord(Data^.FloatType));
      memo.Lines.Add('Float kind: ' + SubName);
    end;

    if Info^.Kind = tkDynArray then
    begin
      { Let's check out the element size  }
      memo.Lines.Add('Array element type name: ' + Data^.elType^^.Name);
      memo.Lines.Add('Array element type size: ' + IntToStr(Data^.elSize));
    end;

  end;
  memo.Lines.Add('Size of type: ' + IntToStr(SizeOf(T)));
end;


procedure TForm2.FormCreate(Sender: TObject);
var
  GenericString : TGenericClass<UnicodeString>;
  GenericByte : TGenericClass<Byte>;
  GenericDouble : TGenericClass<Double>;
//  GenericStringArray : TGenericClass<array of string>;
begin
  { Prints type info for string type}

  GenericString := TGenericClass<UnicodeString>.Create;
  GenericString.PrintTypeInfo(Memo1);

  { Prints type info for Byte type}
  GenericByte := TGenericClass<Byte>.Create;
  GenericByte.PrintTypeInfo(Memo1);

  { Prints type info for Double type}
  GenericDouble := TGenericClass<Double>.Create;
  GenericDouble.PrintTypeInfo(Memo1);
end;

{
Expected results are:

Type name: string
Type kind: tkUString
Size of type: 4

Type name: Byte
Type kind: tkInteger
Integer kind: otUByte
Size of type: 1

Type name: Double
Type kind: tkFloat
Float kind: ftDouble
Size of type: 8

Type name: .1
Type kind: tkDynArray
Array element type name: string
Array element type size: 4
Size of type: 4
}
end.
