问题
Update: The example i originally had was kind of complex. Here's a simple 8 line example that explains everything in one code block. The following does not compile gives a warning:
TComputer = class(TObject)
public
constructor Create(Cup: Integer); virtual;
end;
TCellPhone = class(TComputer)
public
constructor Create(Cup: Integer; Teapot: string); virtual;
end;
Note: This question is part 3 in my ongoing series of questions about the subtlties of constructors in Delphi
Original question
How can i add a constructor to an existing class?
Let's give an hypothetical example (i.e. one that i'm typing up in here in the SO editor so it may or may not compile):
TXHTMLStream = class(TXMLStream)
public
...
end;
Further assume that the normal use of TXHTMLStream
involved performing a lot of repeated code before it can be used:
var
xs: TXHTMLStream;
begin
xs := TXHTMLStream.Create(filename);
xs.Encoding := UTF32;
xs.XmlVersion := 1.1;
xs.DocType := 'strict';
xs.PreserveWhitespace := 'true';
...
xs.Save(xhtmlDocument);
Assume that i want to create a constructor that simplifies all that boilerplate setup code:
TXHTMLStream = class(TXMLStream)
public
constructor Create(filename: string; Encoding: TEncoding); virtual;
end;
constructor TXHTMLStream.Create(filename: string; Encoding: TEncoding);
begin
inherited Create(filename);
xs.Encoding := Encoding;
xs.XmlVersion := 1.1;
xs.DocType := 'strict';
xs.PreserveWhitespace := True;
...
end;
That simplifies usage of the object to:
var
xs: TXHTMLStream;
begin
xs := TXHTMLStream.Create(filename, UTF32);
xs.Save(xhtmlDocument);
Except now Delphi complains that my new constructor hides the old constructor.
Method 'Create' hides virtual method of base type 'TXMLStream'
i certainly didn't mean to hide the ancestor create - i want both.
How do i add a constructor (with a different signature) to a descendant class, while keeping the ancestor constructor so it can still be used?
回答1:
My immediate reaction is to use the overload
keyword, as in:
TCellPhone = class(TComputer)
public
constructor Create(Cup: Integer; Teapot: string); reintroduce; overload; virtual;
end;
Edit: Thanks Ian for the edit, which makes an answer out of my answer. I would like to think that I got it for bravery, so I am going to contribute a fuller example:
program Project1;
{$APPTYPE CONSOLE}
uses
SysUtils;
type
TComputer = class(TObject)
public
constructor Create(Cup: Integer); virtual;
end;
TCellPhone = class(TComputer)
public
constructor Create(Cup: Integer; Teapot: string); reintroduce; overload; virtual;
end;
{ TComputer }
constructor TComputer.Create(Cup: Integer);
begin
writeln('constructed computer: cup = ', Cup);
end;
{ TCellPhone }
constructor TCellPhone.Create(Cup: Integer; Teapot: string);
begin
inherited Create(Cup);
writeln('constructed cellphone: Teapot = ', Teapot);
end;
var
C1, C2, C3: TComputer;
begin
C1 := TComputer.Create(1);
Writeln;
C2 := TCellPhone.Create(2);
Writeln;
C3 := TCellPhone.Create(3, 'kettle');
Readln;
end.
with the result being:
constructed computer: cup = 1
constructed computer: cup = 2
constructed computer: cup = 3
constructed cellphone: Teapot = kettle
回答2:
You could create two new overloaded constructors, for example:
type
TXmlStream = class
private
FFileName: string;
public
constructor Create(const AFileName: string); virtual;
end;
TXhtmlStream = class(TXmlStream)
private
FEncoding: TEncoding;
public
constructor Create(const AFileName: string); overload; override;
constructor Create(const AFileName: string; AEncoding: TEncoding); overload; virtual;
end;
constructor TXmlStream.Create(const AFileName: string);
begin
inherited Create;
FFileName := AFileName;
end;
constructor TXhtmlStream.Create(const AFileName: string);
begin
inherited Create(AFileName);
end;
constructor TXhtmlStream.Create(const AFileName: string; AEncoding: TEncoding);
begin
inherited Create(AFileName);
FEncoding := AEncoding;
end;
回答3:
Another possibility is to write a new constructor with default parameter values where the part of the signature with non-default parameters matches the original constructor in the base class:
type
TXmlStream = class
private
FFileName: string;
public
constructor Create(const AFileName: string); virtual;
end;
TXhtmlStream = class(TXmlStream)
private
FEncoding: TEncoding;
public
constructor Create(const AFileName: string; AEncoding: TEncoding = encDefault); reintroduce; virtual;
end;
constructor TXmlStream.Create(const AFileName: string);
begin
inherited Create;
FFileName := AFileName;
end;
constructor TXhtmlStream.Create(const AFileName: string; AEncoding: TEncoding);
begin
inherited Create(AFileName);
FEncoding := AEncoding;
end;
回答4:
Also remember that constructors don't HAVE to be called Create. Older versions of Delphi didn't have method overloading, so you had to use different names:
TComputer = class(TObject)
public
constructor Create(Cup: Integer); virtual;
end;
TCellPhone = class(TComputer)
private
FTeapot: string;
public
constructor CreateWithTeapot(Cup: Integer; Teapot: string); virtual;
end;
...
constructor TCellPhone.CreateWithTeapot(Cup: Integer; Teapot: string);
begin
Create(Cup);
FTeapot := Teapot;
end;
Both constructors will now be available.
来源:https://stackoverflow.com/questions/3876484/delphi-how-to-add-a-different-constructor-to-a-descendant