tags:

views:

517

answers:

3

Is it possible to create objects at runtime based on it's type by calling a method.

What I am trying to achieve is

var
  lForm1 : TForm;
  lForm2 : TForm;
begin
  CreateObjects([lForm1, lForm2]);
  // After this call I have the variables initialized and I can use them.
end;
+7  A: 

There is insufficient information in the question.

Where does the "type" of the form objects (in the question) come from? Is it simply a type name? How does CreateObjects() discover the type that is required for each object?

It cannot come from the "type" of the object reference passed in, as this may be (and almost certainly will be, as in your example) merely a base type from which the required concrete type will ultimately derive.

Without more detailed information about your specific implementation goals and constraints, a complete, concrete answer is not possible.

However, in general terms what you seek may be achieved by a combination of virtual constructors and the RegisterClass / FindClass infrastructure provided by the VCL.

In simple terms, you would have a base class that introduces the common constructor used to instantiate your classes [for TComponent derived classes this already exists in the form of the Create(Owner: TComponent) constructor].

At runtime you can then obtain a reference to any (registered) class using FindClass('TClassName'). This will return a class reference with which you can then invoke the appropriate virtual constructor:

  type
    TFoo = class ....
    TFooClass = class of TFoo;

    // etc


  var
    someClass: TFooClass;
    someObj: TFoo;
  begin
    someClass := TFooClass(FindClass('TFooDerivedClass'));
    someObj := someClass.Create(nil);
      :

Note in the above that TFooDerivedClass is a class that ultimately derives from TFooClass (and is assumed for simplicity to derive in turn from TComponent and is instantiated with a NIL owner in this case). Classes that are already registered with the type system can be found using FindClass(). This includes any control or component class that is referenced by some DFM in your application. Any additional classes that need to be registered may be explicitly registered using RegisterClass().

How your specific application identifies the types of objects involved and any mapping of type names onto other arbitrary system of identification is an implementation detail that you must take care of.

Deltics
You could invoke the appropriate *non-virtual* constructor and get the same effect. Virtual constructors and class references are related, but not co-dependent, concepts.
Rob Kennedy
Yes, of course - my mistake. Thanks for the clarification.
Deltics
A: 

Hi,

Quoting your comment on Henk's answer :

That's what I don't want to do. I have a lot of server side methods where I create a lot of controls at runtime and I was wondering is creating objects as above would reduce the code.

What do you mean by "a lot"?

If you mean a lot of components of exactly the same type (e.g : "but1, but2, but3, .. but55 : TButton;" ) then change your code and use an array to represent your variables - you can then make a simple loop to create them.

If you mean a lot of components of different types (e.g : but1 : TAnimatedButton; but2 : TFlatButton; but3 : T3DButton;), I don't see any simple method to do this, and I think you would create a small debugging hell more than anything else.

LeGEC
+1  A: 

Untested concept code:

function instantiate(var instancevars : array of tobject;
              const classtypes : array of TBaseClassType):boolean;

begin
  if (length(instancevars)=0) or (length(instancevars)<>length(classtypes)) then
    exit(false);
  for i:=0 to length(instancevars)-1 do
     instancevars[i]:=classtypes[i].create;
  result:=true;
end;

Then use

instantiate([lform1,lform2],[tform1,tform2]); 

to make it work.

Note for this to work "TBaseClassType" must be some baseclass for all classes used for this function, and have a virtual constructor (e.g. TPersistent?). Possibly you also need to correct the .create line (e.g. add (NIL) )

You can't get a type from a variable, the information is only available compiletime.

Marco van de Voort
The constructor only needs to be virtual if descendant classes need to do different things in the constructor. Using a non-virtual constructor will still create instances of the correct type, and all the other virtual methods will still get invoked correctly, including `AfterConstruction`.
Rob Kennedy
You can change TBaseClassType to TObject and it will work for any class.
skamradt
@skamradt overriden constructors will not be called
mjustin
I know, I just didn't want to make it seem overly broad while there are limitations.
Marco van de Voort