Question

Given a text string containing a type name, is there some way to get the appropriate type itself?

I'm looking to do something like this:

type
  TSomeType<T> = class
    // yadda yadda
  end;

procedure DoSomething;
var
  obj : TObject;
begin
  o := TSomeType<GetTypeByName('integer')>.Create;
  // do stuff with obj
end;

I've looked at several RTTI explanations online and looked through the Delphi units and don't see what I'm looking for. Is this possible?

Was it helpful?

Solution

No, generics are entirely compiletime.

OTHER TIPS

The new RTTI unit in Delphi 2010 has a way of retrieving types declared in the interface section of units. For any given type, represented by a TRttiType instance, the TRttiType.QualifiedName property returns a name that can be used with TRttiContext.FindType later to retrieve the type. The qualified name is the full unit name (including namespaces, if they exist), followed by a '.', followed by the full type name (including outer types if it nested).

So, you could retrieve a representation of the Integer type (in the form of a TRttiType) with context.FindType('System.Integer').

But this mechanism can't be used to retrieve instantiations of generic types that weren't instantiated at compile time; instantiation at runtime requires runtime code generation.

You can always register your types into some sort of registry (managed by a string list or dictionary) and create a factory function to then return the appropriate object. Unfortunately you would have to know in advance what types you were going to need. Something similar to the Delphi functions RegisterClass and FindClass (in the classes unit). My thinking is to put the generic template type into the list directly.

An example of possible usage:

RegisterCustomType('Integer',TSomeType<Integer>);
RegisterCustomType('String',TSomeType<String>);

if FindCustomType('Integer') <> nil then
  O := FindCustomType('Integer').Create;

EDIT: Here is a specific simple implementation using a tDictionary from Generics.Collections to handle the registry storage...I'll leave extracting this into useful methods as a simple exercise for the reader.

var
  o : TObject;
begin
  TypeDict := TDictionary<String,TClass>.Create;
  TypeDict.Add('integer',TList<integer>);
  if TypeDict.ContainsKey('integer') then
    o := TypeDict.Items['integer'].Create;
  if Assigned(o) then
    ShowMessage(o.ClassName);
end;

Another EDIT: I was giving this some thought last night, and discovered another technique that you can merge into this concept. Interfaces. Here is a quick do nothing example, but can easily be extended:

TYPE
  ITest = interface
    ['{0DD03794-6713-47A0-BBE5-58F4719F494E}']
  end;

  TIntfList<t> = class(TList<T>,ITest)
  public
    function QueryInterface(const IID: TGUID; out Obj): HRESULT; stdcall;
    function _AddRef: Integer; stdcall;
    function _Release: Integer; stdcall;
  end;

procedure TForm1.Button7Click(Sender: TObject);
var
  o : TObject;
  fTestIntf : ITest;
begin
  TypeDict := TDictionary<String,TClass>.Create;
  TypeDict.Add('integer',TIntfList<integer>);
  if TypeDict.ContainsKey('integer') then
    o := TypeDict.Items['integer'].Create;
  if Assigned(o) and Supports(o,ITest,fTestIntf) then
    ShowMessage(o.ClassName);
end;

of course you would have to implement the QueryInterface, _AddRef and _Release methods and extend the interface to do something more useful.

If you forget generics and basic types, the "RegisterClass" function would be helpful. But it doesn't work for generics or basic types.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top