How do I overload a virtual function introduced in a parent class? - delphi

I have a parent class with one important abstract procedure which I am overloading in many child classes as the example code shown below:
TCParent = Class
private
public
procedure SaveConfig; virtual; abstract;
end;
TCChild = Class(TCParent)
private
public
procedure SaveConfig; override;
end;
Now there I need to (overload) this procedure with another SaveConfig procedure that will accept parameters, yet I don't want to make big changes in the parent class that might require that I go and make changes in all other child classes.
Is there a way I can overload SaveConfig in this specific child class without making big changes to the parent class and other child classes that inherit from it?

You can use reintroduce to add a new overloaded method. Note that the order of reintroduce; overload; in the child class is required; if you reverse them, the code won't compile.
TCParent = Class
private
public
procedure SaveConfig; virtual; abstract;
end;
TCChild = Class(TCParent)
private
public
procedure SaveConfig; overload; override;
procedure SaveConfig(const FileName: string); reintroduce; overload;
end;
(Tested in Delphi 7, so should work in it and all later versions.)

Since you do not want to make changes to other descendants, I would suggest adding an optional field to the parent class to hold the parameters, then any descendant that wants to use parameters can use them. That way, you don't have to change the signature of the overridden SaveConfig(). For example:
type
TCParent = class
protected
SaveConfigParams: TStrings; // or whatever...
public
procedure SaveConfig; overload; virtual; abstract;
procedure SaveConfig(Params: TStrings); overload;
end;
procedure TCParent.SaveConfig(Params: TStrings);
begin
SaveConfigParams := Params;
try
SaveConfig;
finally
SaveConfigParams := nil;
end;
end;
.
type
TCChild = class(TCParent)
public
procedure SaveConfig; override;
end;
procedure TCChild.SaveConfig;
begin
if SaveConfigParams <> nil then
begin
// do something that uses the parameters...
end else begin
// do something else...
end;
end;
.
type
TCChild2 = class(TCParent)
public
procedure SaveConfig; override;
end;
procedure TCChild2.SaveConfig;
begin
// do something, ignoring the SaveConfigParams...
end;

Related

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.

Delphi Data Snap Methods Class - Override Create Constructor

Important I am using Delphi XE 7
I am new to data snap, and I have setup a data snap server and client with the wizard in the menu along side a short tutorial from youtube
The server and client application works and communicates with each other. But in my server methods unit (The unit that contains all the functions that the client can call) I am trying to initialize the variables that I have created in the class (Setting some integers to 0 etc).
I have attempted to override the constructor create, but it would appear as though it is never being called.
I have marked it with virtual, override and overload but none of them seems to be executing this code:
constructor TServerMethods1.create(AOwner: TComponent);
begin
messagedlg('worked', mtError, [mbOk], 0);
inherited;
end;
I originally had the variables in there but it occurred to me that it wasn't executing.
Am I missing something or does it never execute, and if it never executes how does the client call the methods.
Edit - This is the class declaration:
type
tPlayer = record
PlayerName: String;
Spot: Integer;
end;
{$METHODINFO ON}
TServerMethods1 = class(TComponent)
TmrGameStarted: TTimer;
private
{ Private declarations }
fSpot1Taken: Boolean;
fSpot2Taken: Boolean;
fSpot3Taken: Boolean;
fSpot4Taken: Boolean;
fSpot5Taken: Boolean;
Player: Array [1 .. 5] of tPlayer;
fGameStarted: Boolean;
public
{ Public declarations }
fPlayerCount: Integer;
constructor create(AOwner: TComponent); overload;
function getPlayerCount: Integer;
procedure setPlayerCount(i: Integer);
function EchoString(Value: string): string;
function ReverseString(Value: string): string;
function Attempt_Join(sPlayerName: String; sPassword: String): String;
function UpdateLog: String;
function GameStarted: String;
end;
{$METHODINFO OFF}
I have also included the following in my constructor:
constructor TServerMethods1.create(AOwner: TComponent);
begin
messagedlg('worked', mtError, [mbOk], 0);
inherited create(AOwner);
end;
This is the constructor method from System.Classes which is a public declaration constructor Create(AOwner: TComponent); virtual;
{ TComponent }
constructor TComponent.Create(AOwner: TComponent);
begin
FComponentStyle := [csInheritable];
if AOwner <> nil then AOwner.InsertComponent(Self);
end;
This is also added in the type declaration but I have no idea what it does
private class var
FComparer: IComparer<TComponent>;
class constructor Create;
Constructor method:
class constructor TComponent.Create;
begin
FComparer := TDelegatedComparer<TComponent>.Create(
function(const Item1, Item2: TComponent): Integer
begin
Result := CompareText(Item1.Name, Item2.Name);
end);
end;
Okay. I have found the answer to my question. The on create even fires but it fires each time the client calls the function.
After doing some more research it would appear as there isn't an actual way to manipulate the server methods from a form or an application.
Datasnap could be useful depending on what you need it for, In my case I used it in the incorrect way. To use the create constructor just add the override directive. This will override the default parent constructor and execute yours.

Generics constructor with parameter constraint?

TMyBaseClass=class
constructor(test:integer);
end;
TMyClass=class(TMyBaseClass);
TClass1<T: TMyBaseClass,constructor>=class()
public
FItem: T;
procedure Test;
end;
procedure TClass1<T>.Test;
begin
FItem:= T.Create;
end;
var u: TClass1<TMyClass>;
begin
u:=TClass1<TMyClass>.Create();
u.Test;
end;
How do I make it to create the class with the integer param. What is the workaround?
Just typecast to the correct class:
type
TMyBaseClassClass = class of TMyBaseClass;
procedure TClass1<T>.Test;
begin
FItem:= T(TMyBaseClassClass(T).Create(42));
end;
Also it's probably a good idea to make the constructor virtual.
You might consider giving the base class an explicit method for initialization instead of using the constructor:
TMyBaseClass = class
public
procedure Initialize(test : Integer); virtual;
end;
TMyClass = class(TMyBaseClass)
public
procedure Initialize(test : Integer); override;
end;
procedure TClass1<T>.Test;
begin
FItem:= T.Create;
T.Initialize(42);
end;
Of course this only works, if the base class and all subclasses are under your control.
Update
The solution offered by #TOndrej is far superior to what I wrote below, apart from one situation. If you need to take runtime decisions as to what class to create, then the approach below appears to be the optimal solution.
I've refreshed my memory of my own code base which also deals with this exact problem. My conclusion is that what you are attempting to achieve is impossible. I'd be delighted to be proved wrong if anyone wants to rise to the challenge.
My workaround is for the generic class to contain a field FClass which is of type class of TMyBaseClass. Then I can call my virtual constructor with FClass.Create(...). I test that FClass.InheritsFrom(T) in an assertion. It's all depressingly non-generic. As I said, if anyone can prove my belief wrong I will upvote, delete, and rejoice!
In your setting the workaround might look like this:
TMyBaseClass = class
public
constructor Create(test:integer); virtual;
end;
TMyBaseClassClass = class of TMyBaseClass;
TMyClass = class(TMyBaseClass)
public
constructor Create(test:integer); override;
end;
TClass1<T: TMyBaseClass> = class
private
FMemberClass: TMyBaseClassClass;
FItem: T;
public
constructor Create(MemberClass: TMyBaseClassClass); overload;
constructor Create; overload;
procedure Test;
end;
constructor TClass1<T>.Create(MemberClass: TMyBaseClassClass);
begin
inherited Create;
FMemberClass := MemberClass;
Assert(FMemberClass.InheritsFrom(T));
end;
constructor TClass1<T>.Create;
begin
Create(TMyBaseClassClass(T));
end;
procedure TClass1<T>.Test;
begin
FItem:= T(FMemberClass.Create(666));
end;
var
u: TClass1<TMyClass>;
begin
u:=TClass1<TMyClass>.Create(TMyClass);
u.Test;
end;
Another more elegant solution, if it is possible, is to use a parameterless constructor and pass in the extra information in a virtual method of T, perhaps called Initialize.
What seems to work in Delphi XE, is to call T.Create first, and then call the class-specific Create as a method afterwards. This is similar to Rudy Velthuis' (deleted) answer, although I don't introduce an overloaded constructor. This method also seems to work correctly if T is of TControl or classes like that, so you could construct visual controls in this fashion.
I can't test on Delphi 2010.
type
TMyBaseClass = class
FTest: Integer;
constructor Create(test: integer);
end;
TMyClass = class(TMyBaseClass);
TClass1<T: TMyBaseClass, constructor> = class
public
FItem: T;
procedure Test;
end;
constructor TMyBaseClass.Create(test: integer);
begin
FTest := Test;
end;
procedure TClass1<T>.Test;
begin
FItem := T.Create; // Allocation + 'dummy' constructor in TObject
try
TMyBaseClass(FItem).Create(42); // Call actual constructor as a method
except
// Normally this is done automatically when constructor fails
FItem.Free;
raise;
end;
end;
// Calling:
var
o: TClass1<TMyClass>;
begin
o := TClass1<TMyClass>.Create();
o.Test;
ShowMessageFmt('%d', [o.FItem.FTest]);
end;
type
TBase = class
constructor Create (aParam: Integer); virtual;
end;
TBaseClass = class of TBase;
TFabric = class
class function CreateAsBase (ConcreteClass: TBaseClass; aParam: Integer): TBase;
class function CreateMyClass<T: TBase>(aParam: Integer): T;
end;
TSpecial = class(TBase)
end;
TSuperSpecial = class(TSpecial)
constructor Create(aParam: Integer); override;
end;
class function TFabric.CreateAsBase(ConcreteClass: TBaseClass; aParam: Integer): TBase;
begin
Result := ConcreteClass.Create (aParam);
end;
class function TFabric.CreateMyClass<T>(aParam: Integer): T;
begin
Result := CreateAsBase (T, aParam) as T;
end;
// using
var
B: TBase;
S: TSpecial;
SS: TSuperSpecial;
begin
B := TFabric.CreateMyClass <TBase> (1);
S := TFabric.CreateMyClass <TSpecial> (1);
SS := TFabric.CreateMyClass <TSuperSpecial> (1);

Base class's class procedure should instantiate a descendant's object?

Why in the code below, do I get the "Failed" message rather than "Succeeded"
Background: I like to have class procedures that instantiate their owner object, do something, and then free it.
However, this approach doesn't work if I have a descendant object:
Any suggestions on how to provide class procedures in a base class that can be called as a child? Am I thinking about this wrongly?
Type
TBase = class(TObject)
Protected
Procedure Proc1; Virtual;
Public
Class Procedure MyClassProc;
end;
Class Procedure TBase.MyClassProc;
Var
Base: TBase;
begin
Base := TBase.Create;
Base.Proc1;
Base.Free;
end;
Procedure TBase.Proc1;
begin
Assert(FALSE, 'Failed');
end;
type
TChild = class(TBase)
protected
Procedure Proc1; Override;
end;
Procedure TChild.Proc1;
begin
ShowMessage('Succeeded');
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
TChild.MyClassProc;
end;
You can do it easily with meta-programmation! Just change "TBase.Create" to "Self.Create" "self" represents the current class, it doesn't metter if is a base o a child class.
Type
TBase = class(TObject)
Protected
Procedure Proc1; Virtual;
Public
Class Procedure MyClassProc;
end;
Class Procedure TBase.MyClassProc;
Var
MyObject: TBase;
begin
// MyObject := TBase.Create;
MyObject := Self.Create; // The Magic goes here, self is the class that's calling this method, in this case, TChild }
MyObject.Proc1;
MyObject.Free;
end;
Procedure TBase.Proc1;
begin
Assert(FALSE, 'Failed');
end;
type
TChild = class(TBase)
protected
Procedure Proc1; Override;
end;
Procedure TChild.Proc1;
begin
ShowMessage('Succeeded');
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
TChild.MyClassProc;
end;
Strip everything down to the bare minimum, and you will see that you only ever create a TBase instance, so consequently only TBase.Proc1() will ever be called. If you want to have TChild.Proc1() be called you need to create a TChild instance and let polymorphism work its magic.
There could however be better ways to achieve your goal (whatever it is) than to have a class method create an object instance to do something. Maybe you should clarify your question.
Here it is
Add
TBase = class;
TBaseClass = class of TBase;
TBase = class(TObject)
protected
class function GetBaseClass: TBaseClass; virtual;
function TBase.GetBaseClass: TBaseClass;
begin
Result := TBase;
end;
TChild = class(TBase)
protected
class function GetBaseClass: TBaseClass; override;
function TChild.GetBaseClass: TBaseClass;
begin
Result := TChild;
end;
Change
from
Base := TBase.Create;
to
Base := GetBaseClass.Create;
Enjoy your work
Cheer
A Pham

Property override

I have a class TChild derived from TParent. TParent has a property MyProp that is reading and setting some values in an array. Of course this property is inherited by the TChild, but I want to add few extra processing in child's property. The code below explains better what I want to do but it is not working. How can I implement it?
TParent = class...
private
function getStuff(index: integer): integer; virtual;
procedure setStuff(index: integer; value: integer); virtual;
public
property MyProp[index: integer] read GetStuff write SetStuff
end;
TChild = class...
private
procedure setStuff(index: integer; value: integer); override;
function getStuff(index: integer): integer; override;
public
property MyProp[index: integer] read GetStuff write SetStuff
end;
procedure TChild.setStuff(value: integer);
begin
inherited; // <-- execute parent 's code and
DoMoreStuff; // <-- do some extra suff
end;
function TChild.getStuff;
begin
result:= inherited; <---- problem was here
end;
Solved.
The child function implementation was wrong. Basically that code works.
The solution was:
Result := inherited getStuff(Index);

Resources