Friday, April 29, 2011

How can i create a new instance of a class?

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!

From stackoverflow
  • 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.

0 comments:

Post a Comment