how to change ItemClass of a class inherited from TCollection - delphi

I have a class which inherited from TCollection (lets call it "TMyCollection") and I have to inherit a new class from it (lets call it "TMyItems")
Usually we pass the ItemClass type in the constructor of the TCollection but in my case the constructor of TMyCollection is overrided with new constructor which doesn't take ItemClass and it does only take the Owner.
I need to know how to change the ItemClass in "TMyItems" if the inherited constructor doesn't accept ItemClass parameter.
Regards.

You can still call the inherited TCollection.Create from within a subclass, even if it doesn't have the same signature:
TMyCollectionItem = class(TCollectionItem)
private
FIntProp: Integer;
procedure SetIntProp(const Value: Integer);
public
property IntProp: Integer read FIntProp write SetIntProp;
end;
TMyCollection = class(TCollection)
public
constructor Create(AOwner: TComponent);virtual;
end;
{ TMyCollection }
constructor TMyCollection.Create(AOwner: TComponent);
begin
inherited Create(TMyCollectionItem); // call inherited constructor
end;
EDIT :
As per the original poster's comments, a "trick" is to mark the new constructor as overloaded. As it is an overload, it doesn't hide access to the TCollection constructor.
TMyCollection = class(TCollection)
public
constructor Create(AOwner: TComponent);overload; virtual;
end;
TMyItem = class(TCollectionItem)
private
FInt: Integer;
public
property Int: Integer read FInt;
end;
TMyItems = class(TMyCollection)
public
constructor Create(AOwner: TComponent);override;
end;
implementation
{ TMyCollection }
constructor TMyCollection.Create(AOwner: TComponent);
begin
inherited Create(TCollectionItem);
end;
{ TMyItems }
constructor TMyItems.Create(AOwner: TComponent);
begin
inherited Create(TMyItem);
inherited Create(AOwner); // odd, but valid
end;
end.

Related

How can I initialize my custom IdIOHandler fields?

This is blowing my mind... I just want to make a new IdIOHandler, and, as usual, I need to do some initialization in the constructor... Normally, I override the constructor of the base class, but this TIdIOHandlerStack which I inherit from, is far from "normal" ! It has no constructor to override, and is not known (to me) how it is created.
TIdEnhancedIOHandler = class(TIdIOHandlerStack)
private
FSendBuffer: TIdBytes;
FSendBuff: TDataStream;
public
constructor Create; // <-- I tested all the variations here, but non of them work
destructor Destroy; override;
end;
implementation
constructor TIdEnhancedIOHandler.Create;
begin
inherited Create;
FSendBuff:= TDataStream.Create(#SendBuffer);
end;
destructor TIdEnhancedIOHandler.Destroy;
begin
FSendBuff.Free;
inherited;
end;
initialization
TIdEnhancedIOHandler.SetDefaultClass;
Where should I put my intitialization code so that it is executed when a new instance of TIdEnhancedIOHandler is created BY DEFAULT in all Indy Components which use IOHandlers ?
I found it... It was the InitComponent method that I must override.
TIdEnhancedIOHandler = class(TIdIOHandlerStack)
private
FSendBuffer: TIdBytes;
FSendBuff: TDataStream;
public
procedure InitComponent; override;
destructor Destroy; override;
end;
implementation
procedure TIdEnhancedIOHandler.InitComponent;
begin
inherited;
FSendBuff:= TDataStream.Create(#SendBuffer);
end;

Delphi: How to overload the parent constructor in a descendant but hide it on other different descendant

I read this answer about constructors and its directives (reintroduce, overload, virtual, override, etc.) but I can't reach the goal I want. Check the following pseudo-code (I mean the code without any directive yet):
TBaseClass = class
constructor Create;
end;
TStringStuff = class (TBaseClass)
constructor Create(str: string);
end;
TNumberStuff = class (TBaseClass)
constructor Create(num: integer);
constructor Create(num: decimal);
end;
I want a TStringStuff object can be created using its own constructor and the parent one:
var
StrStuff: TStringStuff;
begin
StrStuff:=TStringStuff.Create();
//or
StrStuff:=TStringStuff.Create('bla');
end;
but I also want a TNumberStuff object can be created using ONLY its own constructors, i.e. if someone use my library he wont be able to create a TNumberStuff without parameter:
var
NumStuff: TNumberStuff ;
begin
NumStuff:=TNumberStuff.Create(10);
//or
NumStuff:=TNumberStuff.Create(10.5);
// but NOT: NumStuff:=TNumberStuff.Create();
end;
So how to use the directives to achieve my goals?
(I am using Delphi 10.2 Tokyo)
For a variety of reasons you can't achieve exactly what you want, but this is as close as you can get.
I have introduced a 'superbase' class if you like with a hidden constructor, only visible in that unit. The only function of TBaseClass now is to 'expose' the constructor so that you can create instances of TBaseClass.
unit Test1;
interface
type
TBaseBaseClass = class
// This does all the work of TBaseClass, but hides the contructor
private
constructor Create; reintroduce; // this can only be accessed within this unit
end;
TBaseClass = class(TBaseBaseClass)
// a creatable class. No actual work is done here. It's only purpose is to
// 'expose' the base constructor
public
constructor Create; reintroduce;
end;
TStringStuff = class( TBaseBaseClass )
public
constructor Create; reintroduce; overload;
constructor Create( str : string ); reintroduce; overload;
end;
TNumStuff = class( TBaseBaseClass )
public
constructor Create( num : integer ); reintroduce; overload;
constructor Create( num : single ); reintroduce; overload;
end;
implementation
{ TStringStuff }
constructor TStringStuff.Create(str: string);
begin
inherited Create;
// ...
// other stuff
end;
constructor TStringStuff.Create;
begin
inherited Create;
// does no extra work! 'exposes' TBaseBaseClass constructor
// but required because of rules of polymorphism
end;
{ TBaseBaseClass }
constructor TBaseBaseClass.Create;
begin
inherited Create;
// ...
// other stuff - does the work originally in TBaseClass
end;
{ TBaseClass }
constructor TBaseClass.Create;
begin
inherited Create;
// does no extra work! 'exposes' TBaseBaseClass constructor
end;
{ TNumStuff }
constructor TNumStuff.Create(num: single);
begin
inherited Create;
// ...
// other stuff
end;
constructor TNumStuff.Create(num: integer);
begin
inherited Create;
// ...
// other stuff
end;
end.
In another unit, if you put a test procedure like this
procedure Test;
var
iBaseClass : TBaseClass;
iStringStuff : TStringStuff;
iNumStuff : TNumStuff;
begin
iBaseClass := TBaseClass.Create;
iStringStuff := TStringStuff.Create;
iNumStuff := TNumStuff.Create;
end;
you will find it does not compile.
But there are a couple of 'gotchas'. If you try putting this procedure in the same unit as the original definitions it will compile. That is because the TBaseBase constructor is visible within the unit.
The second gotcha is related to the fact that the hidden constructor is parameterless, and there is a public parameterless constructor for TObject, from which all objects are descended. So if you try to create an instance of TBaseBaseClass using a constructor without parameters it will compile. It just won't use the constructor you might expect. It will use the TObject constructor.
Finally I would advise against ever trying to hamstring other programmers. By all means lead them in the right direction, but don't try and stop them doing what they want to do. With that in mind, I would not do it this way. I would make the TBaseBase constructor protected, not private. I am just showing this to answer your question.
Here is how I wold deal with your problem
First I would set BaseClass constructor to be virtual and thus allow overriding it in desendant classes
TBaseClass = class
constructor Create; virtual;
end;
Then in TStringStuff class I would change your existing constructor so that its string parameter is actually an optional parameter. This would allow you to call this constructor with or without string parameter passed to it. So now ony thing you need to do is call parent constructor with the help of inherited when no parameter was passed to consturctor or do necessary work before if string paramter was passed to the constructor.
TStringStuff = class (TBaseClass)
constructor Create(str: string = ''); override;
end;
And in TNumberStuff class you just reintroduce your overloaded constructors to be able to manage multiple posible input parameter types that can be passed to the constructor.
TNumberStuff = class (TBaseClass)
constructor Create(num: integer); reintroduce; overload;
constructor Create(num: decimal); reintorduce; overload;
end;

Can constructors be private? [duplicate]

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.

Object doesn't creating

I'm creating a non-visual component. Short Code:
// Element of some list item
TMyItem = class
private
Id: integer;
Caption: string;
public
constructor Create(const aId: integer; const aCaption: string);
end;
// List of items
TMyItemList = class(TObjectList<TMyItem>)
public
constructor Create;
end;
// The component
TMyComp = class(TComponent)
private
FMyList: TMyItemList;
public
constructor Create;
implementation
constructor TMyComp.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FMyList:= TMyItemList.Create;
end;
Question: why "FMyList" not exists when "MyComp" created? Assigned(FMyList) = false ...
This is not your actual code. Your actual code compiles. But we can guess what the problem is. Here:
type
TMyComp = class(TComponent)
private
FMyList: TMyItemList;
public
constructor Create;
end;
The constructor for TComponent is declared like this:
constructor Create(AOwner: TComponent); virtual;
It's a virtual constructor because the Delphi streaming framework relies upon metaclasses to create objects. If you want the framework to call your constructor you have to override this virtual constructor.
constructor Create(AOwner: TComponent); override;
I'm reasonably confident of this guess, but we should not need to guess. Next time, please submit real code, a Minimal, Complete, and Verifiable Example.

Using an inherited list of objects

I'm trying to create an object structure where one object contains a list of other objects. I have a base class for both of these, similar to TCollection/TCollectionItem but a very custom implementation.
type
TMyItemBase = class;
TMyListBase = class;
TMyItemBaseClass = class of TMyItemBase;
TMyItemBase = class(TObject)
private
FOwner: TMyListBase;
public
constructor Create(AOwner: TMyListBase);
destructor Destroy; override;
property Owner: TMyListBase read FOwner;
end;
TMyListBase = class(TMyObjectBase)
private
FItems: TList;
FItemClass: TMyItemBaseClass;
function New: TMyItemBase;
function GetItem(Index: Integer): TMyItemBase;
public
constructor Create(AOwner: TMyMainObject; const ItemClass: TMyItemBaseClass);
destructor Destroy; override;
function Count: Integer;
procedure Clear;
property Items[Index: Integer]: TMyItemBase read GetItem; default;
end;
implementation
{ TMyItemBase }
constructor TMyItemBase.Create(AOwner: TMyListBase);
begin
FOwner:= AOwner;
end;
destructor TMyItemBase.Destroy;
begin
inherited;
end;
{ TMyListBase }
constructor TMyListBase.Create(AOwner: TMyMainObject; const ItemClass: TMyItemBaseClass);
begin
inherited Create(AOwner);
FItems:= TList.Create;
FItemClass:= ItemClass;
end;
destructor TMyListBase.Destroy;
begin
Clear;
FItems.Free;
inherited;
end;
procedure TMyListBase.Clear;
begin
while FItems.Count > 0 do begin
TMyItemBase(FItems[0]).Free;
FItems.Delete(0);
end;
end;
function TMyListBase.Count: Integer;
begin
Result:= FItems.Count;
end;
function TMyListBase.GetItem(Index: Integer): TMyItemBase;
begin
Result:= TMyItemBase(FItems[Index]);
end;
function TMyListBase.New: TMyItemBase;
begin
Result:= FItemClass.Create(Self);
FItems.Add(Result);
end;
(Pseudo code, sorry if there are any typos)
Problem is, when a new item is created (via TMyListBase.New), the object is successfully created, but the inheritance and all its fields are not (The inherited object's constructor is never even called)...
type
TMyItem = class;
TMyItems = class;
TMyItem = class(TMyItemBase)
private
//various unrelated fields
public
constructor Create;
destructor Destroy; override;
//various unrelated properties
end;
TMyItems = class(TMyListBase)
private
function GetItem(Index: Integer): TMyItem;
function New: TMyItem;
public
constructor Create(AOwner: TMyMainObject);
destructor Destroy; override;
property Items[Index: Integer]: TMyItem read GetItem; default;
end;
implementation
{ TMyItem }
constructor TMyItem.Create;
begin
inherited;
//Initialize some fields
end;
destructor TMyItem.Destroy;
begin
//Destroy some fields
inherited;
end;
{ TMyItems }
constructor TMyItems.Create(AOwner: TMyMainObject);
begin
inherited Create(AOwner, TMyItem);
end;
destructor TMyItems.Destroy;
begin
inherited;
end;
function TMyItems.GetItem(Index: Integer): TMyItem;
begin
Result:= TMyItem(inherited Channels[Index]);
end;
function TMyItems.New: TMyItem;
begin
Result:= TMyItem(inherited New);
end;
It appears to be something wrong with the New function, but I cannot figure it out. Even though I'm creating the item as its intended item class, it gets further treated as if it were the base class, and none of the inherited members are accessible (giving access violations) because the inherited constructor is never called.
What am I overlooking / doing wrong here?
Your TMyItemBase.Create() constructor needs to be declared as virtual, and then descendant classes need to override it. This is important when constructing objects using metaclass types. For example:
type
TMyItemBase = class(TObject)
...
public
constructor Create(AOwner: TMyListBase); virtual;
...
end;
constructor TMyItemBase.Create(AOwner: TMyListBase);
begin
inherited Create;
FOwner := AOwner;
end;
type
TMyItem = class(TMyItemBase)
...
public
constructor Create(AOwner: TMyListBase); override;
...
end;
constructor TMyItem.Create(AOwner: TMyListBase);
begin
inherited Create(AOwner);
...
end;
Try to use generics!
type
TMyItem = class
//Implement your class here
end;
//Use it now!
var
MyItemsList:TObjectList<TMyItem>;
// or implement your generic class customization:
type
TVeryMyItemsList = class(TObjectList<TMyItem>)
end;
// Or implement your generic class
type
TMyGeneric<T> = class(TObjectList<T>)
end;
//where T is any type

Resources