Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations Mike Lewis on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Polymorphism? Inheritence? question

Status
Not open for further replies.

rodneyb3

Programmer
Jul 24, 2007
6
AU
I came across an interesting situation that I shall try to describe.

type
TObject1 = class(TObject)
....
end;

TObject2 = class(TObject)
....
end;

implimentation

procedure ABC(aObject: TObject);
var
LocalObject: TObject;
begin
LocalObject := TypeOf(aObject).Create;
:
LocalObject.Free;
end;


var
MyObject2: TObject2;
begin
ABC(MyObject2);
end;

I would like to pass the ABC procedure a descendant of TObject, and inside the ABC procedure create an instance of the descendant object (not of TObject) and copy the object that was passed in. In the above example, 'TypeOf' of course does not compile. At run time, the type of the aObject is known so it should be possible to create an object of whatever type aObject is and assign it to the LocalObject variable. I just don't know how?

I would rather not do the following solution;

procedure ABC(aObject: TObject);
var
LocalObject: TObject;
begin
if aObject is TObject1 then
LocalObject := TObject1.Create;
if aObject is TObject2 then
LocalObject := TObject2.Create;
:
end;

Can anyone help???
Thanks
 
Why don't you define the Tobject2 as a child of TObject1 and make the procedure a constructor of TObject1.
if you want lots of child objects then include a TList as part of Object1 and use this to hold the children.

A Constructor procedure of the child object can use the inherited keyword to call the Object1 constructor then create any none inherited fields.

Yes I know some examples would be better here but I cant find the ones I want on this machine and I don't want to post something that might not work. So I hope this is clear.






Steve [The sane]: Delphi a feersum engin indeed.
 
'Why don't you define the Tobject2 as a child of TObject1 and make the procedure a constructor of TObject1.'

For the same reason you wouldn't make a brother the child if his sister!

TObject1 & TObject2 have the same parent, but other than that they have no further relationship.
 
Code:
[navy][i]// for automatic syntax highlighting see faq102-6487 
[/i][/navy][b]procedure[/b] ABC(AClass: TClass);
[b]var[/b]
  o : TObject;
[b]begin[/b]
  o := AClass.Create;
[b]end[/b];

[b]procedure[/b] CallABC;
[b]begin[/b]
  ABC(TObject1);
[b]end[/b];
 
Nice try, but this does not quite do it.
The AClass.Create seems to create an object of the type TObject1, but does not call the constructor of TObject1. It calls the constructor of TObject only. In the simple examples above, the TObject1's create is the same as TObject, but in a slightly more realistic example, it isn't.
I've included below my latest code for experimenting with this problem.

The message box shows 'Obj2 0' when the button is pressed, indicating that LocalObject is of type TMyObject2 but the TObject2.Create was not called!

Any further help would be greatly appreciated.

Code:
unit Unit29;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TParent = class(TObject)
    public
      Hello: integer;
      constructor Create; virtual;
    end;

  TMyObject1 = class(TParent)
    public
      Num1: integer;
      constructor Create; override;
    end;

  TMyObject2 = class(TParent)
    public
      Num2: integer;
      constructor Create; override;
    end;

  TForm1 = class(TForm)
    Button1: TButton;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
    fObj1: TMyObject1;
    fObj2: TMyObject2;
    procedure ABC(aObject: TParent);
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

constructor TParent.Create;
begin
  Hello := 0;
end;

constructor TMyObject1.Create;
begin
  Num1 := 100;
end;

constructor TMyObject2.Create;
begin
  Num2 := 200;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  fObj1 := TMyObject1.Create;
  fObj2 := TMyObject2.Create;
end;

procedure TForm1.ABC(aObject: TParent);
var
  LocalObject: TObject;
begin
  LocalObject := aObject.ClassType.Create;
       if LocalObject is TMyObject2 then ShowMessage('Obj2 '+ IntToStr(TMyObject2(LocalObject).Num2))
  else if LocalObject is TMyObject1 then ShowMessage('Obj1')
  else if LocalObject is TObject    then ShowMessage('Obj');
  FreeAndNil(LocalObject);
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  ABC(fObj2);
end;

end.
 
First up, you should always cal the inherited Create in your constructors. This is true with any Create override.

I see you deviated from my code above in order to get your code to compile. You need to add a few things to get it working.

first, declare a new class reference
Code:
TParentClass = [b]class of[/b] TParent;

then, change your ABC header to
Code:
procedure TForm1.ABC(aObject: TParentClass);
var
  LocalObject: TParent;
begin
  LocalObject := aObject.Create;
  // snip

and, call ABC by using the type declaration of your object, rather than the object itself.

Code:
ABC(TMyObject2);

 
I should add, this is necessary because TObject's Create constructor is not virtual. Your LocalObject var cannot reference a descendant object, which is why only the TObject's Create gets called.

When we shift things up to the TParent level, TParent's Create constructor is virtual, therefore it will always call the most descended constructor.
 
Gryffn,
My objective with this code was to pass only an object to the ABC routine and to be able to generate a local copy of it to edit (the ABC routine will actually be an edit form). If the user hits an OK button then the local copy of the object will be Assigned to the original (aObject), else discarded.

Through your help, I think the code below finally does what I want. (output is 'Obj2 999')

I have really valued your comments and guidence, it has been much appreciated, thanks.

Code:
unit Unit29;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TParent = class(TObject)
    public
      Hello: integer;
      constructor Create; virtual;
      procedure Assign(aObject: TObject); virtual; abstract;
    end;

  TParentClass = class of TParent;

  TMyObject1 = class(TParent)
    public
      Num1: integer;
      constructor Create; override;
      procedure Assign(aObject: TObject); override;
    end;

  TMyObject2 = class(TParent)
    public
      Num2: integer;
      constructor Create; override;
      procedure Assign(aObject: TObject); override;
    end;

  TForm1 = class(TForm)
    Button1: TButton;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
    fObj1: TMyObject1;
    fObj2: TMyObject2;
    procedure ABC(aObject: TParent);
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

constructor TParent.Create;
begin
  Hello := 0;
end;

constructor TMyObject1.Create;
begin
  inherited;

  Num1 := 100;
end;

procedure TMyObject1.Assign(aObject: TObject);
begin
  if not (aObject is TMyObject1) then exit;

  Num1 := TMyObject1(aObject).Num1;
end;

constructor TMyObject2.Create;
begin
  inherited;

  Num2 := 200;
end;

procedure TMyObject2.Assign(aObject: TObject);
begin
  if not (aObject is TMyObject2) then exit;

  Num2 := TMyObject2(aObject).Num2;
end;


procedure TForm1.FormCreate(Sender: TObject);
begin
  fObj1 := TMyObject1.Create;
  fObj2 := TMyObject2.Create;
end;

procedure TForm1.ABC(aObject: TParent);
var
  LocalObject: TParent;
begin
  LocalObject := TParentClass(aObject.ClassType).Create;
  LocalObject.Assign(aObject);
       if LocalObject is TMyObject2 then ShowMessage('Obj2 '+ IntToStr(TMyObject2(LocalObject).Num2))
  else if LocalObject is TMyObject1 then ShowMessage('Obj1')
  else if LocalObject is TObject    then ShowMessage('Obj');
  FreeAndNil(LocalObject);
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  fObj2.Num2 := 999;
  ABC(fObj2);
end;

end.
 
Perhaps I wasnt clear but what you are doing here is exactly what I was getting at.

And please give Griffyn a Star
star.gif

if you found his post helpful.


Code:
type
  TParent = class(TObject)
    public
      Hello: integer;
      constructor Create; virtual;
      procedure Assign(aObject: TObject); virtual; abstract;
    end;

  TParentClass = class of TParent;

  TMyObject1 = class(TParent)
    public
      Num1: integer;
      constructor Create; override;
      procedure Assign(aObject: TObject); override;
    end;

Steve [The sane]: Delphi a feersum engin indeed.
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top