开发者

Delphi - Create class from a string

开发者 https://www.devze.com 2023-02-28 16:31 出处:网络
I got code like this name := \'Foo\'; If name = \'Foo\' then result := TFoo.Create else if name = \'开发者_运维百科Bar\' then

I got code like this

name := 'Foo';
If name = 'Foo' then
  result := TFoo.Create
else if name = '开发者_运维百科Bar' then 
  result := TBar.Create
else if name = 'FooFoo' then
  result := TFooFoo.Create;

Is there a way just to do

result := $name.create

or some way of creating class based of a variable value?

All the classes extended the same base class.


Starting with Delphi 2010, the enhanced RTTI allows you do this without having to creating your own Class Registry.

Using the RTTI Unit you have several options available.

For Parameter Less Constructors one of the easiest is.

var
 C : TRttiContext;
 O : TObject;
begin
  O := (C.FindType('UnitName.TClassName') as TRttiInstanceType).MetaClassType.Create;
  ...
 end;

Here is an example of passing a parameter, using the TRttiMethod.Invoke()

var
 C : TRttiContext;
 T : TRttiInstanceType;
 V : TValue;

begin
  T := (C.FindType('StdCtrls.TButton') as TRttiInstanceType);
  V := T.GetMethod('Create').Invoke(T.metaClassType,[self]);
  (V.AsObject as TWinControl).Parent := self;
end;

I wrote several articles on the RTTI unit as there is many options available.


Updated Based on David Request:

Comparing the usage of construction using the Class Type (Virtual Constructor) with the TRttiType.Invoke

Class Type Method: (Virtual Constructor)

  • Works in all version of Delphi
  • Produces Faster Code
  • Requires knowledge of ancestry at compile time.
  • Requires a Class Registry to look up a Class by a String Name (Such as mentioned by RRUZ)

TRttiType.Invoke() method

  • Only works in Delphi 2010 or later.
  • Slower code
  • Implements a Class Registry that takes Name conflicts into account
  • Requires NO knowledge of ancestry at compile time.

I personally find each serves a different purpose. If I know all the types up front the I use the Class Type Method.


You can use the GetClass function, but before you must register the classes using the RegisterClass or RegisterClasses methods.

GetClass(const AClassName: string): TPersistentClass;


The normal way to do this is with virtual constructors. A good example is TComponent which you are no doubt familiar.

TComponent has the following constructor:

constructor Create(AOwner: TComponent); virtual;

The other key to this is TComponentClass which is declared as class of TComponent.

When the VCL streams .dfm files it reads the name of the class from the .dfm file and, by some process that we don't need to cover here, converts that name into a variable, ComponentClass say of type TComponentClass. It can then instantiate the object with:

Component := ComponentClass.Create(Owner);

This is the big advantage of having a virtual constructor and I would encourage you to take the same approach.

If you have to use a string to identify the class then you'll still need to come up with a lookup routine to convert from the string class name to a class reference. You could, if convenient, hook into the same VCL mechanism that TComponent uses, namely RegisterClass.

Alternatively if you could replace name in your code with a class reference then you could write:

type
  TFoo = class
    constructor Create; virtual;
  end;
  TBar = class(TFoo);

  TFooClass = class of TFoo;

var
  MyClass: TFooClass;

...

MyClass := TFoo;
result := MyClass.Create;//creates a TFoo;

MyClass := TBar;
result := MyClass.Create;//creates a TBar;
0

精彩评论

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

关注公众号