I have the following classes declared in a single file:
type
TCongruence = class(TObject)
private
a, b, n, numClass: integer;
solutions, vals: TSolutions;
hasSolutions: boolean;
function divides(a, b: integer): boolean;
function modulo(a, b: integer): integer;
public
constructor Create(a, b, n: integer); virtual;
function getSolutions: TSolutions; virtual;
function gcdExtended(p, q: integer): TSolutions;
class function getGCD(u, v: integer): integer;
property valA: integer read a;
property valB: integer read b;
property valN: integer read n;
property getClass: integer read numClass;
property hasSol: boolean read hasSolutions;
end;
type
TConguenceSystem = class(TCongruence)
private
system: array of TCongruence;
public
constructor Create(a: array of TCongruence); override;
function getSolutions: integer; override;
end;
The second one as you can see is a subclass because I need to use all the functions implemented in the TCongruence class. I have declared the constructor virtual so that I can call the override on the descendant.
Is that correct? Do I have to remove the virtual/override and simply use the constructor like this? (below)
constructor Create(a: array of TCongruence);
I guess that in this case I am hiding the father's constructor. I have declared this constructor:
constructor TConguenceSystem.Create(a: array of TCongruence);
var i: integer;
begin
SetLength(system, length(a)); // private var system: array of TCongruence
for i := Low(a) to High(a) do
begin
system[i] := a[i];
end;
solutions := false;
end;
When you intend to override behaviour of a method with the same signature in a descendent class then you must declare it virtual in the base class and the descendent class would then use override.
If, however, you wish to introduce a new method with a different signature then you must use the overload directive if you are declaring the method within the same class. This simply allows re-use of the same method name for what are, effectively, entirely different methods with different signatures. For example :
TCongruence = class(TObject)
public
constructor Create(a : integer); overload;
constructor Create(a, b, n: integer); overload;
end;
If you are declaring a new method in a descendent class, however, with a different signature then none of these decorations are required.
TCongruence = class(TObject)
public
constructor Create(a, b, n: integer);
end;
TCongruenceSystem = class(TCongruence)
public
constructor Create(a: array of TCongruence);
end;
The above is fine - you are not overriding the original constructor, you are simply introducing a new one with a new signature. Since the latter belongs to a new class with a different name there is no ambiguity and overload is not required. You can even access the ancestor methods in the usual way here :
TCongruence = class(TObject)
private
Fa, Fb, Fn : integer;
public
constructor Create(a, b, n: integer);
end;
TCongruenceSystem = class(TCongruence)
private
FArr : array of TCongruence;
public
constructor Create(a: array of TCongruence);
end;
constructor TCongruence.Create(a, b, n: integer);
begin
inherited Create;
Fa := a;
Fb := b;
Fn := n;
end;
constructor TCongruenceSystem.Create(a: array of TCongruence);
var
c : TCongruence;
i : integer;
begin
inherited Create(a[0].Fa, a[1].Fb, a[2].Fn);
SetLength(FArr, Length(a));
i := 0;
for c in a do begin
FArr[i] := c;
Inc(i);
end;
end;
Without the overload directive, however, the following would not be allowed :
var
cs : TCongruenceSystem;
begin
cs := TCongruenceSystem.Create(1, 2, 3);
end.
since TCongruenceSystem is hiding the base class Create which takes three integer arguments. If you allow the overload, however :
TCongruence = class(TObject)
private
Fa, Fb, Fn : integer;
public
constructor Create(a, b, n: integer); overload;
end;
TCongruenceSystem = class(TCongruence)
private
FArr : array of TCongruence;
public
constructor Create(a: array of TCongruence); overload;
end;
Then the above call of cs := TCongruenceSystem.Create(1, 2, 3); would be allowed and the ancestor constructor would be used to build the descendent class.
These approaches can be combined, for example :
TCongruence = class(TObject)
public
constructor Create(a : integer); overload; virtual; {overridable}
constructor Create(a, b, n: integer); overload; {only in base}
end;
TCongruenceSystem = class(TCongruence)
public
constructor Create(a:integer); overload; override; {overrides parent}
constructor Create(a: string); overload; {introduce new}
end;
In the case of the constructor you are introducing a method with a different set of parameters so this is allowed. In the case of getSolutions, however, the function is takes no parameters and differs only in the return type. An overloaded method needs to have a different parameter set, however, so this type of mutation is not allowed in a descendent class. getSolutions in the descendent class would need to take a different name if you intend it to also be a parameterless function with a different return type.
Related
Take a look at this class:
TTest = class(TObject)
public
constructor Create(A:Integer);overload;
constructor Create(A,B:Integer);overload;
end;
Now when we want to use the class:
var
test: TTest;
begin
test:= TTest.Create; //this constructor is still visible and usable!
end;
Can anyone help me with hiding this constructor?
So long as you have overloaded constructors named Create, you cannot hide the parameterless TObject constructor when deriving from TObject.
This is discussed here: http://www.yanniel.info/2011/08/hide-tobject-create-constructor-delphi.html
If you are prepared to put another class between your class and TObject you can use Andy Hausladen's trick:
TNoParameterlessContructorObject = class(TObject)
strict private
constructor Create;
end;
TTest = class(TNoParameterlessContructorObject)
public
constructor Create(A:Integer);overload;
constructor Create(A,B:Integer);overload;
end;
You can hide the inherited Create by just introducing a non overloaded Create. As you need two overloaded Create, you can either merge those into one Create with an optional second parameter:
TTest = class(TObject)
public
constructor Create(A:Integer; B: Integer = 0);
end;
This will give a compiler warning, signalling that you're hiding the default parameterless constructor. To get rid of the warning you can declare the hiding constructor like so:
TTest = class(TObject)
public
constructor Create(A:Integer; B: Integer = 0); reintroduce;
end;
or, if this is not feasible, you can introduce an intermediate class introducing the first create and then the final class with the overloaded second one:
preTest = class(TObject)
public
constructor Create(A:Integer); reintroduce;
end;
TTest = class(preTest)
public
constructor Create(A,B:Integer);overload;
end;
Another option is to use the deprecated keyword and raise an exception at runtime.
TTest = class(TObject)
public
constructor Create; overload; deprecated 'Parameterless constructor is not Supported for a TTest class';
constructor Create(const A: Integer); overload;
constructor Create(const A, B: Integer); overload;
end;
implementation
constructor TTest.Create;
begin
raise Exception.Create('Parameterless constructor is not Supported for a TTest class.');
end;
Through the two inheritance, user creation of TMySingleton class can be prevented from design time rather than runtime.
unit MySingleton;
interface
uses System.Classes, System.SysUtils;
type
// Constructor Block external access
THideConstructor = class abstract
strict protected
constructor Create; virtual; abstract;
end;
// Switching the access to the Create function THideConstructor in TObject through the constructor Overloading
// Declaring Create Method as a procedure to prevent class call-TMySingle.Create('string') call impossible
TOverloadConstructor = class(THideConstructor)
public
procedure Create(s: string); reintroduce; overload; deprecated 'null method';
end;
TMySingleton = class sealed(TOverloadConstructor)
private
class var MyObj: TMySingleton;
strict protected
// Hiding TOverloadConstructor.Create(s: string);
// Implement THideConstructor.Create
constructor Create; override;
public
class function Obj: TMySingleton;
function Echo(const value: string): String;
destructor Destroy; override;
end;
implementation
{ TMySingleton }
constructor TMySingleton.Create;
begin
// TODO
end;
destructor TMySingleton.Destroy;
begin
Self.MyObj := nil;
inherited;
end;
function TMySingleton.Echo(const value: string): String;
begin
result := value;
end;
class function TMySingleton.Obj: TMySingleton;
begin
if MyObj = nil then
MyObj := Self.Create;
result := MyObj;
end;
{ TOverloadContructor }
procedure TOverloadConstructor.Create(s: string);
begin
// null method
end;
initialization
TMySingleton.MyObj := nil;
finalization
if Assigned(TMySingleton.MyObj) then
FreeAndNil(TMySingleton.MyObj);
end.
If the user
var
Singleton: TMySingleton;
begin
Singleton := TMySingleton.Create;
Design-time error occurs.
[dcc32 Error] Unit1.pas(33): E2625 Private member 'THideConstructor.Create' is inaccessible here MySingleton.pas(11): Related method: constructor Create;
enter image description here
Also, you can't see any autocomplete hints named Create.
I'm experimenting with Delphi 10 Seattle and trying to create my first Generic Container class. I need help with a Generic Comparer
Here a simple Hash object which I created:
type
TsmHeap<T> = class
private
fList: TList<T>;
Comparer: TComparer<T>;
procedure GetChildren(ParentIndex: integer; var Child1, Child2: integer);
function GetParent(ChildIndex: integer): integer;
function GetCapacity: integer;
function GetCount: integer;
function MustSwap(iParent, iChild: integer): boolean;
procedure SetCapacity(const Value: integer);
public
constructor Create(aComparer: TComparer<T>); overload;
constructor Create(aComparer: TCOmparer<T>; aCapacity: integer); overload;
destructor Destroy; override;
//-- Methods & Functions
function Dequeue: T;
procedure Enqueue(Item: T);
function IsEmpty: boolean;
//-- Properties
property Count: integer read GetCount;
property Capacity: integer read GetCapacity write SetCapacity;
end;
I've write the code for the methods and it compiles on its own with no problems. However when I try to create an integer version of the class I cannot get it to compile.
The problematic code is:
iHeap := TsmHeap<integer>.Create(TComparer<integer>.Construct(
function(const Left, Right: integer): integer
begin
result := Sign(Left - Right);
end)
);
This give a "E2250 There is no overloaded version of 'Create' that can be called with these arguments"
What am I doing wrong? How do I create the Comparer?
TComparer<T>.Construct returns IComparer<T> - it is a class function and not a constructor. Just change the parameter type of TsmHeap<T>.Create to IComparer<T> and it should work.
How can I expose this TList from an interface, as either IEnumerator or IEnumerator<IFungibleTroll>? I am using Delphi XE.
Here's how far I got:
unit FungibleTrollUnit;
interface
uses
Windows, Messages, SysUtils,
Variants, Classes, Graphics,
Controls, Forms,
Generics.Collections;
type
IFungibleTroll = interface
['{03536137-E3F7-4F9B-B1F5-2C8010A4D019}']
function GetTrollName:String;
function GetTrollRetailPrice:Double;
end;
TFungibleTrolls = class (TInterfacedObject,IEnumerable<IFungibleTroll>)
protected
FTrolls:TList<IFungibleTroll>;
public
// IEnumerable
function GetEnumerator:IEnumerator<IFungibleTroll>;//
// function GetEnumerator:IEnumerator; overload;
// find/search app feature requires searching.
// this
function FindSingleItemByName(aName:String;patternMatch:Boolean):IFungibleTroll;
function FindMultipleItemsByName(aName:String;patternMatch:Boolean):IEnumerable<IFungibleTroll>;
function FindSingleItemByIdentifier(anIdentifer:String):IFungibleTroll;// use internal non-visible identifier to find an app.
constructor Create;
property Trolls:TList<IFungibleTroll> read FTrolls; // implements IEnumerable<IFungibleTroll>;??
private
end;
implementation
{ TFungibleTrolls }
constructor TFungibleTrolls.Create;
begin
FTrolls := TList<IFungibleTroll>.Create;
end;
function TFungibleTrolls.FindMultipleItemsByName(aName: String;
patternMatch: Boolean): IEnumerable<IFungibleTroll>;
begin
end;
function TFungibleTrolls.FindSingleItemByIdentifier(
anIdentifer: String): IFungibleTroll;
begin
end;
function TFungibleTrolls.FindSingleItemByName(aName: String;
patternMatch: Boolean): IFungibleTroll;
begin
end;
function TFungibleTrolls.GetEnumerator: IEnumerator<IFungibleTroll>;
begin
result := FTrolls.GetEnumerator;
end;
//function TFungibleTrolls.GetEnumerator: IEnumerator;
//begin
// result := FTrolls.GetEnumerator; // type IEnumerator<IFungibleTroll> or IEnumerator?
//end;
end.
I get stuck in one of three errors that I can't figure out how to solve:
[DCC Error] FungibleTrollUnit.pas(26): E2252 Method 'GetEnumerator' with identical parameters already exists
-or-
[DCC Error] FungibleTrollUnit.pas(19): E2291 Missing implementation of interface method IEnumerable.GetEnumerator
-or-
[DCC Error] FungibleTrollUnit.pas(19): E2291 Missing implementation of interface method IEnumerable.GetEnumerator
It seems I must declare two forms of GetEnumerator, if I declare TFungibleTrolls to implement IEnumerable, but I can't seem to figure out how to do it, either with overloads, or without overloads, or using a "method resolution clause", like this:
function IEnumerable.GetEnumerator = GetPlainEnumerator; // method resolution clause needed?
function GetEnumerator:IEnumerator<IFungibleTroll>;
function GetPlainEnumerator:IEnumerator;
This probably seems like a pretty basic use of IEnumerable, and making an Interface support iteration, and yet, I'm stuck.
Update: It seems when I try to do this without first declaring a List<T>, I am falling into a crack caused by the fact that IEnumerable<T> inherits from IEnumerable, and yet, instead of a single get enumerator method, my class must provide multiple ones, and because my class is not a generic, it can't "map itself" to IEnumerable's requirements directly unless I use a generic List<T> declaration. Marjan's sample works, when compiled into a project (.dproj+.dpr) but not when built into a package (.dproj+.dpk) and compiled in the IDE. It works fine from the command line, in a package, but not in the IDE, in a package.
Not an answer to your question directly (still working on that), but this is what I did to get an "interfaced enumerator", ie and interfaced class that supports iteration:
IList<T> = interface(IInterface)
[...]
function GetEnumerator: TList<T>.TEnumerator;
function Add(const Value: T): Integer;
end;
type
TBjmInterfacedList<T> = class(TBjmInterfacedObject, IList<T>)
strict private
FList: TList<T>;
function GetEnumerator: TList<T>.TEnumerator;
strict protected
function Add(const Value: T): Integer;
public
constructor Create; override;
destructor Destroy; override;
end;
implementation
constructor TBjmInterfacedList<T>.Create;
begin
inherited;
FList := TList<T>.Create;
end;
destructor TBjmInterfacedList<T>.Destroy;
begin
FreeAndNil(FList);
inherited;
end;
function TBjmInterfacedList<T>.GetEnumerator: TList<T>.TEnumerator;
begin
Result := FList.GetEnumerator;
end;
function TBjmInterfacedList<T>.Add(const Value: T): Integer;
begin
Result := FList.Add(Value);
end;
And then you can do stuff like:
ISite = interface(IInterface)
...
end;
ISites = interface(IList<ISite>);
...
end;
var
for Site in Sites do begin
...
end;
with implementing classes like:
TSite = class(TBjmInterfacedObject, ISite)
...
end;
TSites = class(TBjmInterfacedList<ISite>, ISites)
...
end;
Update
Example project source uploaded to
http://www.bjmsoftware.com/delphistuff/stackoverflow/interfacedlist.zip
If you really want to make a class that implements IEnumerable<T>, you can do it like this:
unit uGenericEnumerable;
interface
uses SysUtils, Classes, Generics.Collections;
type TGenericEnumerator<T> = class(TInterfacedObject, IEnumerator, IEnumerator<T>)
private
FList: TList<T>;
FIndex: Integer;
protected
function GenericGetCurrent: T;
public
constructor Create(AList: TList<T>);
procedure Reset;
function MoveNext: Boolean;
function GetCurrent: TObject;
function IEnumerator<T>.GetCurrent = GenericGetCurrent;
property Current: T read GenericGetCurrent;
end;
type TNonGenericEnumerable = class(TInterfacedObject, IEnumerable)
protected
function GetNonGenericEnumerator: IEnumerator; virtual; abstract;
public
function IEnumerable.GetEnumerator = GetNonGenericEnumerator;
end;
type TGenericEnumerable<T> = class(TNonGenericEnumerable, IEnumerable<T>)
private
FList: TList<T>;
public
constructor Create;
destructor Destroy; override;
function GetNonGenericEnumerator: IEnumerator; override;
function GetEnumerator: IEnumerator<T>;
property List: TList<T> read FList;
end;
implementation
{ TGenericEnumerator<T> }
constructor TGenericEnumerator<T>.Create(AList: TList<T>);
begin
inherited Create;
FList := AList;
FIndex := -1;
end;
procedure TGenericEnumerator<T>.Reset;
begin
FIndex := -1;
end;
function TGenericEnumerator<T>.MoveNext: Boolean;
begin
if FIndex < FList.Count then
begin
Inc(FIndex);
Result := FIndex < FList.Count;
end
else
begin
Result := False;
end;
end;
function TGenericEnumerator<T>.GenericGetCurrent: T;
begin
Result := FList[FIndex];
end;
function TGenericEnumerator<T>.GetCurrent: TObject;
begin
// If T has not been constrained to being a class, raise an exception instead of trying to return an object.
raise Exception.Create('Cannot use this as a non-generic enumerator');
// If T has been constrained to being a class, return GenericGetCurrent.
// Result := GenericGetCurrent;
end;
{ TGenericEnumerable<T> }
constructor TGenericEnumerable<T>.Create;
begin
inherited Create;
FList := TList<T>.Create;
end;
destructor TGenericEnumerable<T>.Destroy;
begin
FList.Free;
end;
function TGenericEnumerable<T>.GetEnumerator: IEnumerator<T>;
begin
Result := TGenericEnumerator<T>.Create(FList);
end;
function TGenericEnumerable<T>.GetNonGenericEnumerator: IEnumerator;
begin
Result := GetEnumerator;
end;
end.
Now, your FungibleTrollUnit will look something like this:
unit FungibleTrollUnit;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics,
Controls, Forms, Generics.Collections,
uGenericEnumerable;
type
IFungibleTroll = interface
['{03536137-E3F7-4F9B-B1F5-2C8010A4D019}']
function GetTrollName:String;
function GetTrollRetailPrice:Double;
end;
IFungibleTrolls = interface(IEnumerable<IFungibleTroll>)
['{090B45FB-2925-4BFC-AE97-5D3F54E1C575}']
function GetTrolls: TList<IFungibleTroll>;
function FindSingleItemByName(aName:String):IFungibleTroll;
function FindMultipleItemsByName(aName:String):IEnumerable<IFungibleTroll>;
property Trolls:TList<IFungibleTroll> read GetTrolls;
end;
TFungibleTrolls = class (TGenericEnumerable<IFungibleTroll>, IFungibleTrolls, IEnumerable<IFungibleTroll>)
public
function GetTrolls: TList<IFungibleTroll>;
function FindSingleItemByName(aName: String): IFungibleTroll;
function FindMultipleItemsByName(aName: String): IEnumerable<IFungibleTroll>;
property Trolls:TList<IFungibleTroll> read GetTrolls;
private
end;
implementation
uses StrUtils;
{ TFungibleTrolls }
function TFungibleTrolls.GetTrolls: TList<IFungibleTroll>;
begin
Result := List;
end;
function TFungibleTrolls.FindMultipleItemsByName(aName: String): IEnumerable<IFungibleTroll>;
var FilteredTrolls: TGenericEnumerable<IFungibleTroll>;
var Troll: IFungibleTroll;
begin
FilteredTrolls := TGenericEnumerable<IFungibleTroll>.Create;
for Troll in List do
begin
if Troll.GetTrollName = aName then
FilteredTrolls.List.Add(Troll);
end;
Result := IEnumerable<IFungibleTroll>(FilteredTrolls);
end;
function TFungibleTrolls.FindSingleItemByName(aName: String): IFungibleTroll;
var Troll: IFungibleTroll;
begin
Result := nil;
for Troll in List do
begin
if Troll.GetTrollName = aName then
Result := Troll;
break;
end;
end;
end.
Note that the implementation of IEnumerable does not work, but IEnumerable<T> does work.
This is because, unless T is constrained, you cannot convert a T to a TObject.
If T is a string or an integer, for example, then the IEnumerator does not have a TObject to return.
If T is constrained to be a class, you can get the implementation of IEnumerable to work.
If you constrain T to be an IInterface, you could get IEnumerable to work (Delphi 2010 and after (cast GenericGetCurrent to an IInterface, then to a TObject)), but I doubt if that is an advantage.
I would rather use it without the constraints, and do without being able to iterate everything as TObjects.
TGenericEnumerable<T>.GetEnumerator can't use FList.GetEnumerator because TList<T>.GetEnumerator does not return an IEnumerator<T>
Even though you can implement TGenericEnumerable<T> without defining TNonGenericEnumerable, like this:
type TGenericEnumerable<T> = class(TInterfacedObject, IEnumerable, IEnumerable<T>)
private
FList: TList<T>;
protected
function GenericGetEnumerator: IEnumerator<T>;
public
constructor Create;
destructor Destroy; override;
function GetEnumerator: IEnumerator;
function IEnumerable<T>.GetEnumerator = GenericGetEnumerator;
property List: TList<T> read FList;
end;
the disadvantage of doing this is that if you try to iterate using the TGenericEnumerable<T> object, rather than the interface, GetEnumerator will be non-generic and you can only iterate TObjects.
Usual caveats about mixing references to an interface and its underlying object. If you refer to an object both as an object type and as an IEnumerable<....>, then when the interface reference count goes back to zero, the object will be freed even if you still have a reference to it as an object. (That's why I defined IFungibleTrolls; so that I could refer to the collection as an interface).
You can make alternative implementations of TGenericEnumerator<T>; for example, it could contain a reference to a list, an index and a selection predicate, which are all supplied in the constructor.
Yet another in my series of questions regarding constructors in Delphi.
i have a base class that has has the virtual constructor:
TComputer = class(TObject)
public
constructor Create(Teapot: Integer); virtual;
end;
The constructor is virtual for the times that someone needs to call
var
computerClass: class of TComputer;
computer: TComputer;
begin
computer := computerClass.Create(nTeapot);
The constructor is overridden in descendants:
TCellPhone = class(TComputer)
public
constructor Create(Teapot: Integer); override;
end;
TiPhone = class(TCellPhone )
public
constructor Create(Teapot: Integer); override;
end;
Where TCellPhone and TiPhone descendants each have their opportunity to do their own initialization (of members not included for readability).
But now i add an overloaded constructor to some ancestor:
TCellPhone = class(TComputer)
public
constructor Create(Teapot: Integer); override; overload;
constructor Create(Teapot: Integer; Handle: string); overload;
end;
The alternate constructor in TCellPhone calls the other virtual constructor, so it always gets the proper overridden behaviour:
constructor TCellPhone.Create(Teapot: Integer; Handle: string);
begin
TCellPhone.Create(Teapot); //call sibling virtual constructor
FHandle := Handle;
end;
The problem is that the descendant, overridden, constructor is never called. The actual stack trace chain of calls is:
phone := TiPhone.Create(37, 'spout')
constructor TCellPhone.Create(Teapot: Integer; Handle: string)
constructor TCellPhone.Create(Teapot: Integer)
constructor TComputer.Create(Teapot: Integer)
TObject.Create
The sibling call to TCellPhone.Create(int), which is virtual, should have called the descendant, overridden, method in TiPhone:
phone := TiPhone.Create(37, 'spout')
constructor TCellPhone.Create(Teapot: Integer; Handle: string)
constructor TiPhone.Create(Teapot: Integer)
constructor TCellPhone.Create(Teapot: Integer)
constructor TComputer.Create(Teapot: Integer)
TObject.Create
So it seems that attempts to use a sibling virtual constructor is Delphi do not work as expected.
Is it then a bad idea for one constructor to use another? Is the design intention that code in overloaded constructors be copy-paste versions of each other?
i notice in .NET that some constructors chain to each other:
public Bitmap(int width, int height) : this(width, height, PixelFormat.Format32bppArgb) {}
public Bitmap(int width, int height, PixelFormat format) {...}
This only seems to be a problem if:
a constructor is virtual
you overload the constructors
Is the rule that you cannot have one constructor overload another?
Errr..
constructor TCellPhone.Create(Teapot: Integer; Handle: string);
begin
TCellPhone.Create(Teapot); //call sibling virtual constructor
FHandle := Handle;
end;
That should be:
constructor TCellPhone.Create(Teapot: Integer; Handle: string);
begin
Create(Teapot); //call sibling virtual constructor
FHandle := Handle;
end;
You were just creating a new TCellphone instance and not calling the other Create method.
How can I create an instance of an object using a class reference, and
ensure that the constructor is executed?
In this code example, the constructor of TMyClass will not be called:
type
TMyClass = class(TObject)
MyStrings: TStrings;
constructor Create; virtual;
end;
constructor TMyClass.Create;
begin
MyStrings := TStringList.Create;
end;
procedure Test;
var
Clazz: TClass;
Instance: TObject;
begin
Clazz := TMyClass;
Instance := Clazz.Create;
end;
Use this:
type
TMyClass = class(TObject)
MyStrings: TStrings;
constructor Create; virtual;
end;
TMyClassClass = class of TMyClass; // <- add this definition
constructor TMyClass.Create;
begin
MyStrings := TStringList.Create;
end;
procedure Test;
var
Clazz: TMyClassClass; // <- change TClass to TMyClassClass
Instance: TObject;
begin
Clazz := TMyClass; // <- you can use TMyClass or any of its child classes.
Instance := Clazz.Create; // <- virtual constructor will be used
end;
Alternatively, you can use a type-casts to TMyClass (instead of "class of TMyClass").
Alexander's solution is a fine one but does not suffice in certain situations. Suppose you wish to set up a TClassFactory class where TClass references can be stored during runtime and an arbitrary number of instances retrieved later on.
Such a class factory would never know anything about the actual types of the classes it holds and thus cannot cast them into their according meta classes. To invoke the correct constructors in such cases, the following approach will work.
First, we need a simple demo class (don't mind the public fields, it's just for demonstration purposes).
interface
uses
RTTI;
type
THuman = class(TObject)
public
Name: string;
Age: Integer;
constructor Create(); virtual;
end;
implementation
constructor THuman.Create();
begin
Name:= 'John Doe';
Age:= -1;
end;
Now we instantiate an object of type THuman purely by RTTI and with the correct constructor call.
procedure CreateInstance();
var
someclass: TClass;
c: TRttiContext;
t: TRttiType;
v: TValue;
human1, human2, human3: THuman;
begin
someclass:= THuman;
// Invoke RTTI
c:= TRttiContext.Create;
t:= c.GetType(someclass);
// Variant 1a - instantiates a THuman object but calls constructor of TObject
human1:= t.AsInstance.MetaclassType.Create;
// Variant 1b - same result as 1a
human2:= THuman(someclass.Create);
// Variant 2 - works fine
v:= t.GetMethod('Create').Invoke(t.AsInstance.MetaclassType,[]);
human3:= THuman(v.AsObject);
// free RttiContext record (see text below) and the rest
c.Free;
human1.Destroy;
human2.Destroy;
human3.Destroy;
end;
You will find that the objects "human1" and "human2" have been initialized to zero, i.e., Name='' and Age=0, which is not what we want. The object human3 instead holds the default values provided in the constructor of THuman.
Note, however, that this method requires your classes to have constructor methods with not parameters. All the above was not conceived by me but explained brillantly and in much more detail (e.g., the c.Free part) in Rob Love's Tech Corner.
Please check if overriding AfterConstruction is an option.
Your code slightly modified:
type
TMyObject = class(TObject)
MyStrings: TStrings;
constructor Create; virtual;
end;
TMyClass = class of TMyObject;
constructor TMyObject.Create;
begin
inherited Create;
MyStrings := TStringList.Create;
end;
procedure Test;
var
C: TMyClass;
Instance: TObject;
begin
C := TMyObject;
Instance := C.Create;
end;
You can create an abstract method in base class, and call it in the constructor, and override in child classes to be executed when created from class reference.