i have a list of class instances of various kinds. i need to be able to create a new instance of a class without knowing for sure what to create. all the objects involved have the same ancestor. the actual copying of the object's member variables is easy...it's the creation of the new object where i have a problem.
admittedly i could do something like this:
case MyObjectTypeInstance.MyTypeEnum of
obj1:
Result:=TObjectType1.Create;
obj2:
Result:=TObjectType2.Create;
obj3:
Result:=TObjectType3.Create;
end;
that wouldn't follow the "open/closed principle".
initially i thought i could do something like "Result:=MyObjectTypeInstance.Create;" but that didn't work as hoped because of destructor difficulties.
here's the latest guess how i should be doing this...
var
fooA, fooB:TFoo;
begin
fooA:=TFoo2.Create; // it could be any of many types
fooB:=? // how to create fooB of same class type as fooA????
// do something
fooA.Free;
fooB.Free;
end;
i would've thought this'd be easier!
thank you for your help!
-
Option 1 - create a list of name/class mappings: Is there a way to instantiate a class by its name in delphi?
Option 2 - use a 'of class' variable.
type TBaseObj = class end; TObjA = class(TBaseObj) end; TBaseObjClass = class of TBaseObj; var objCls: TBaseObjClass; obj: TBaseObj; objCls := TObjA; obj := objCls.Create; //obj is of type TObjA
X-Ray : thank you gabr! in fact, thank you for your massive contribution to StackOverflow. -
If all classes have a common ancestor, you can do something like this:
type TAncestor = class; TAncestorClass = class of TAncestor; TAncestor = class public constructor Create; virtual; class function CreateClass(const AId: string): TAncestor; class procedure RegisterClass(const AId: string; const AType: TAncestorClass); end; class function TAncestor.CreateClass(const AId: string): TAncestor; var atype : TAncestorClass; begin atype := GetAncestorClass(AId); if atype<>nil then Result := atype.Create else Result := nil; end; class procedure TAncestor.RegisterClass(const AId: string; const AType: TAncestorClass); begin SetAncestorClass(AId, AType); // Link id to class type end;
You can use any kind of identification for the type registration. As long as they are unique.
X-Ray : thank you Gamecat! in fact, thank you for your massive contribution to StackOverflow.Gamecat : Hey, we're in it together ;-). But thanks. -
You will probably want to create an Abstract Factory or Factory Method class. These are common Design Patterns which are tested, proven development paradigms.
X-Ray : yes...of course! i should've thought of that! thank you! -
thank you all for your answers!
dar7yl's solution suited my needs perfectly.
type TFoo = class private { private declarations } public { public declarations } class function MakeAnother:TFoo; end; TFoo1 = class(TFoo) private { private declarations } public { public declarations } end; TFoo2 = class(TFoo) private { private declarations } public { public declarations } end; var fooA, fooB:TFoo; begin fooA:=TFoo2.Create; foob:=fooA.MakeAnother; // do something here fooA.Free; fooB.Free; end; { TFoo } class function TFoo.MakeAnother: TFoo; begin Result:=Create; end;
Jamo : Thanks for posting this follow-up to your question/example -- greatly helpful to "lurkers and 'laters' with similar questions." : )X-Ray : you're welcome. i'm always grateful to see followups like that so i try to do them myself!avar : thanks for the code example.Oliver Giesen : hmm, if this is what your solution now looks like then it seems I must have misunderstood the question then... Seems to me you're still explicitly specifying which class to create...X-Ray : i needed another instance of the same object. perhaps my question wasn't so great. -
Another, messier version is using "class of type" and TObject.ClassType
type TFoo = class private { private declarations } public { public declarations } constructor Create(WhatEver : Integer);virtual;// just to show need for params end; TFooClass = class of TFoo; TFoo1 = class(TFoo) private { private declarations } public { public declarations } constructor Create(WhatEver : Integer);override;// just to show need for params end; TFoo2 = class(TFoo) private { private declarations } public { public declarations } end; {$R *.dfm} procedure TForm10.Button1Click(Sender: TObject); var fooA, fooB:TFoo; begin fooA:=TFoo2.Create(0); fooB:= TFooClass(FooA.ClassType).Create(1); // do something here fooA.Free; fooB.Free; end; { TFoo } constructor TFoo.Create(WhatEver: Integer); begin ShowMessageFmt('%s %d', [Self.ClassName, WhatEver]); end; { TFoo1 } constructor TFoo1.Create(WhatEver: Integer); begin inherited; end;
Heinrich Ulbricht : Wouldn't call it messy, but rather flexible :) I used it recently to create clone-functions where the base class is able to create the right child-class instance.