Jump to content
A.M. Hoornweg

Create a new instance of a generic class

Recommended Posts

Hello all,

 

suppose I have a generic class that's going to be  inherited.

 

I want to give the base class a method that creates a new instance of the same object type (a class factory so to speak).  How can I do that? I need to somehow tell the compiler which constructor to call.  

 

 

 

Type tSomeclass<T:iInterface>=CLass (tInterfacedObject, iSomeInterface<T>)
     //many methods here
     Procedure SomeMethod(intf: T);
     Function SomeFunction:T;
     Constructor Create(someparameters);
     Function CreateNewInstance:iSomeInterface<T>;
  end;
    
  ...
  
  Function tSomeclass<T>.CreateNewInstance:iSomeInterface<T>
  begin
      result:=Self.Classtype.Create(fSomeParameters); //Does not compile
  end;
  

 

Share this post


Link to post

Ha, delphi 10.4 has a bug here. 

 

type
iSomeInterface<T> = interface

end;
Type tSomeclass<T:iInterface>=CLass (tInterfacedObject, iSomeInterface<T>)
     Constructor Create(s: string);
     Function CreateNewInstance:iSomeInterface<T>;
  end;


  Function tSomeclass<T>.CreateNewInstance:iSomeInterface<T>;
  type
    TSelfClass = tSomeclass<T>;
    TSomeClassType = class of TSelfClass; // !!!!!!!!!!!! [dcc32 Error] Unit9.pas(36): E2021 Class type required
  begin
      result:=TSomeClassType(Self.Classtype).Create('s'); //Does not compile
  end;

 

Share this post


Link to post

Even if you could get this to compile, you need to make the constructor virtual in order to call a derived constructor from a base metaclass type.

Share this post


Link to post

Even if it would compile this way, it is quite possible that it does not do what you expect it to do:

  result := tSomeclass<T>(Classtype).Create(fSomeParameters);

 

Share this post


Link to post
6 hours ago, A.M. Hoornweg said:

Type tSomeclass<T:iInterface>=CLass (tInterfacedObject, iSomeInterface<T>)
     //many methods here
     Procedure SomeMethod(intf: T);
     Function SomeFunction:T;
     Constructor Create(someparameters);
     Function CreateNewInstance:iSomeInterface<T>;
  end;
    
  ...
  
  Function tSomeclass<T>.CreateNewInstance:iSomeInterface<T>
  begin
      result:=Self.Classtype.Create(fSomeParameters); //Does not compile
  end;
  

 

I tHiNk YoU NeEd To DeBoUnCe YoUr ShIfT KeYs...

 

I think you can get something to compile but even then I have a feeling that, whatever it is you are trying to do, isn't possible.

FWIW, this one compiles:

type
  IMyInterface<T> = interface
    function GetValue: T;
    function Clone: IMyInterface<T>;
  end;

  TMyClass<T> = class(TInterfacedObject, IMyInterface<T>)
  private
    FValue: T;
  private
    // IMyInterface<T>
    function GetValue: T;
    function Clone: IMyInterface<T>;
  public
    constructor Create(const AValue: T); virtual;
    function CloneObject: TMyClass<T>;
  end;

function TMyClass<T>.Clone: IMyInterface<T>;
begin
  Result := CloneObject;
end;

function TMyClass<T>.CloneObject: TMyClass<T>;
begin
  Result := TMyClass<T>.Create(FValue);
end;

constructor TMyClass<T>.Create(const AValue: T);
begin
  FValue := AValue;
end;

function TMyClass<T>.GetValue: T;
begin
  Result := FValue;
end;

 

Share this post


Link to post

That doesn't do the trick, because 

 

function TMyClass<T>.CloneObject: TMyClass<T>;

... will just construct an instance of the base class,  I would have to re-implement that method in each derived class.

 

With non-generic classes it is possible to achieve what I want (see below).  With generics, the "class of..." syntax does not work.

 

type
  SomeClassType = class of tSomeClass;

  tSomeClass = class
  protected
    SomeNumber: Integer;
  public
    constructor Create(aSomeNumber: Integer); virtual;
    function Copy: tSomeClass;
    procedure Whoami;
  end;

  tSomeClass2=Class(tSomeClass)
  End;

  tSomeClass3=Class(tSomeClass2)
  End;


function tSomeClass.Copy: tSomeClass;
var ctype: SomeClassType;
begin
  ctype := SomeClassType(self.ClassType);
  result := ctype.Create(SomeNumber+1);
end;

constructor tSomeClass.Create(aSomeNumber: Integer);
begin
  inherited Create;
  SomeNumber := aSomeNumber;
end;

procedure tSomeClass.Whoami;
begin
  OutputDebugString(pchar( format('Class name = %s, number is %d',[self.classname,somenumber]  )));
end;


procedure TForm1.Button2Click(Sender: TObject);
begin
     tSomeclass3.Create(1).Copy.Copy.Copy.Whoami; //yes, this test leaks some memory.
end;

 

 

 

 

Share this post


Link to post
1 hour ago, A.M. Hoornweg said:

That doesn't do the trick, because


function TMyClass<T>.CloneObject: TMyClass<T>;

... will just construct an instance of the base class,  I would have to re-implement that method in each derived class.

Yes, you are right - and you couldn't do it with polymorphism.

Share this post


Link to post

You can trick/hack a bit and do what you want to achieve manually:

 

This is basically what a regular ctor call on the class type does (see System._ClassCreate).

function TSomeClass<T>.CreateNewInstance: ISomeInterface<T>;
var
  obj: TSomeClass<T>;
begin
  obj := TSomeClass<T>(ClassType.NewInstance);
  obj.Create(fSomeParameters);
  Result := obj;
end;

 

Edited by Stefan Glienke
Fixed typo
  • Like 1

Share this post


Link to post
25 minutes ago, Stefan Glienke said:

This is basically what a regular ctor call on the class type does (see System._CreateClass).

Where is System._CreateClass defined?  I cannot find it in any RTL source file, and when I do a test creating a class instance from a metaclass, there is no such function call.

Share this post


Link to post
3 hours ago, Remy Lebeau said:

Where is System._CreateClass defined?  I cannot find it in any RTL source file, and when I do a test creating a class instance from a metaclass, there is no such function call.

Sorry, I meant _ClassCreate - the compiler inserts a call to that routine in the prologue of each ctor. When calling the ctor on a class the compiler passes 1 as the second parameter which signals this routine to call TObject.NewInstance.

Edited by Stefan Glienke

Share this post


Link to post


The reason I'm trying to achieve this is the following.

 

I have made a generic tStreamableInterfaceList<T:iInterface> which is basically a managed Tlist<T> that implements iInterfacelist<T>. The elements are interfaces.

 

This list object knows several special tricks such as saving its contents to XML and BSON.  It can also load those elements back from such data files, which necessitates a virtual ClassFactory() method to create the correct objects "on the fly" based on the class names found in the data. So far, this all works very nicely. 
 

I wanted to enhance this list with a few LinQ-like methods such as:

 

Function .Where(aCondition:tPredicate<T>):iInterfacelist<T>;
Function .OrderBy(aCompare:tComparison<T>):iInterfacelist<T>;

 
 ... but in order to achieve that, the base class must be able to dynamically instantiate derived classes or else the resulting objects would have the base nonfunctional Classfactory() method.   The "ugly" solution would be to put an abstract virtual Clone() method in the base class, but I'd very much like to avoid that.

Share this post


Link to post
13 hours ago, Stefan Glienke said:

You can trick/hack a bit and do what you want to achieve manually:

 

This is basically what a regular ctor call on the class type does (see System._ClassCreate).


function TSomeClass<T>.CreateNewInstance: ISomeInterface<T>;
var
  obj: TSomeClass<T>;
begin
  obj := TSomeClass<T>(ClassType.NewInstance);
  obj.Create(fSomeParameters);
  Result := obj;
end;

 

 

Thank you! That seems to work indeed !

 

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×