A class function that instantiates itself in Delphi

You have a class function that instantiates the class:

TMyClass = class(TSomeParent) public class function New(AValue : integer) : TMyClass; end; TDerivedClass = class(TMyClass) public function Beep; end; 

and then use it as follows

 ... var myList : TList<T>; item : TDerivedClass; begin myList.Add(TDerivedClass.New(1)) myList.Add(TDerivedClass.New(3)) myList.Add(TDerivedClass.New(5)) for item in myList do item.Beep; //times the count in the class function ... 

And if so, what does this function code look like? Do you use the TObject NewInstance method and do you constantly execute it for each derived class? Is saver / better to use a constructor?

The goal is to use this approach in a command template and load a list of commands with class types and receiver, for example:

 //FYI: document is an instance of TDocument commandList.Execute(TOpenDocument(document)); commandList.Execute(TPasteFromClipboard(document)); //... lots of actions - some can undo commandList.Execute(TPrintDocument(document)); commandList.Execute(TSaveDocument(document)); 

And the reason is that some commands will be given using text / script and should be allowed at runtime.

+7
source share
5 answers

You have a class function that instantiates a class.
Is saver / better to use a constructor?

A constructor is a class function that instantiates a class. Just put:

 constructor New(); virtual; 

And you are good to go.

virtual; part virtual; allows you to call the same constructor New () for all descendant classes.

+2
source

What you are looking for is called a factory pattern. This can be done in Delphi; this is how VCL deserializes forms, by the way. What you are missing is part of the registration / search in the system. Here is the basic idea:

  • Somewhere you created a registration table. If you use Delphi XE, you can implement this as TDictionary<string, TMyClassType> , where TMyClassType is defined as a class of TMyClass . It is important. You need a map between class names and types of class types.
  • Put the virtual constructor on TMyClass. Everything that comes from it will use this constructor or its override when creating the factory template.
  • When you create a new descendant class, call the method that registers with the registration table. This should happen when the program starts, either in initialization or in the class constructor.

When you need to create something from a script, do it like this:

  class function TMyClass.New(clsname: string; [other params]): TMyClass; begin result := RegistrationTable[clsName].Create(other params); end; 

You use the registration table to get a reference to the class from the class name, and call the virtual constructor in the class link to get the correct object type from it.

+12
source

Yes, it is technically possible to create an instance from a class method, just call the actual constructor and then return the instance that it creates, for example:

 type TMyClass = class(TSomeParent) public constructor Create(AValue : Integer); virtual; class function New(AValue : integer) : TMyClass; end; TDerivedClass = class(TMyClass) public constructor Create(AValue : Integer); override; function Beep; end; constructor TMyClass.Create(AValue : Integer); begin inherited Create; ... end; function TMyClass.New(AValue : integer) : TMyClass; begin Result := Create(AValue); end; constructor TDerivedClass.Create(AValue : Integer); begin inherited Create(AValue); ... end; var myList : TList<TMyClass>; item : TMyClass; begin myList.Add(TDerivedClass.New(1)) myList.Add(TDerivedClass.New(3)) myList.Add(TDerivedClass.New(5)) for item in myList do TDerivedClass(item).Beep; 

In this case, you better just use the constructor directly:

 type TMyClass = class(TSomeParent) end; TDerivedClass = class(TMyClass) public constructor Create(AValue : Integer); function Beep; end; var myList : TList<TDerivedClass>; item : TDerivedClass; begin myList.Add(TDerivedClass.Create(1)) myList.Add(TDerivedClass.Create(3)) myList.Add(TDerivedClass.Create(5)) for item in myList do item.Beep; 
+5
source

Another option is to use RTTI. The code below works like a normal method in my class as a way to get a new instance of an object with a subset of elements, but since the elements (together with the list object itself) are probably descendants, creating an instance of the object in which this method is defined is not good enough, so how it should be of the same type of instance.

i.e.

 TParentItem = Class End; TParentList = Class Items : TList<TParentItem>; Function GetSubRange(nStart,nEnd : Integer) : TParentList; End; TChildItem = Class(TParentItem) end TChildList = Class(TParentList) end List := TChildList.Create; List.LoadData; SubList := List.GetSubRange(1,3); 

Implementation if GetSubRange will be something like ...

 Function TParentList.GetSubRange(nStart,nEnd : Integer) : TParentList; var aContext: TRttiContext; aType: TRttiType; aInsType : TRttiInstanceType; sDebug : String; begin aContext := TRttiContext.Create; aType := aContext.GetType(self.ClassType); aInsType := aType.AsInstance; Result := aInsType.GetMethod('Create').Invoke(aInsType.MetaclassType,[]).AsType<TParentList>; sDebug := Result.ClassName; // Should be TChildList // Add the items from the list that make up the subrange. End; 

I appreciate for some things that this may be a bit OTT, but in the project above it works and is another alternative, although I appreciate it is not a class method.

+1
source

You must use the constructor (a special "view" of the class function). TObject.NewInstance not a good option unless you need special memory allocation.

And with regard to the Execute procedure of the command list: now the action involved depends on the type of object. Imagine that a document can be opened, printed, inserted and saved at the same time (rather than a strange assumption), which would be difficult to implement in this structure. Instead, consider adding interfaces (IOpenDocument, IPasteFromClipboard, IPrintable, ISaveDocument), which can actually be actions of a single instance of a document.

0
source

All Articles