问题
I have basically this object structure:
TJSONStructure = class(TObject);
TReqBase = class(TJSONStructure)
private
token: Int64;
public
procedure FillWithTemplateData; virtual;
end;
TReqLogin = class(TReqBase)
private
username,
password: String;
module : Integer;
public
procedure FillWithTemplateData; override;
end;
procedure TReqBase.FillWithTemplateData;
begin
token := ...;
end;
procedure TReqLogin.FillWithTemplateData;
begin
inherited;
username := ...;
password := ...;
module := ...;
end;
type
TWebAct = (ttlogin,
ttsignin);
TReqClass = class of TReqBase;
const
cWebActStructures: Array[TWebAct] of
record
RequestClass : TReqClass;
end
= (
{ ttlogin } (RequestClass: TReqLogin;),
{ ttsignin } (RequestClass: TReqSignIn;) // Not in definitions above
);
Now I do:
var
lWebAct : TWebAct;
lRequestClass : TReqClass;
begin
for lWebAct := Low(TWebAct) to High(TWebAct) do
begin
lRequestClass := cWebActStructures[lWebAct].RequestClass;
and I want to call
lRequestClass.FillWithTemplateData;
in order to execute TReqLogin.FillWithTemplateData
when lWebAct = ttlogin
etc.
But it won't compile: E2706 This form of method call only allowed for class methods.
I do understand the reason (the text of the compiler message) but how can I fix this so that TReqLogin.FillWithTemplateData gets executed when lWebAct=ttlogin etc without having to handle a list of TReqLogin, TReqSignIn types in the code (again)?
回答1:
lRequestClass
is a class reference. You can call class
methods on it, but not instance methods. And FillWithTemplateData
is an instance method.
You need to have an instance to call an instance method. So instantiate one:
var
req: TReqBase;
....
req := lRequestClass.Create;
try
req.FillWithTemplateData;
...
finally
req.Free;
end;
If you develop the classes so that they need to perform work in their constructors then you must introduce a virtual constructor to TReqBase
. And override that in derived classes. That's the only way that you can make sure that the derived constructor runs when you are instantiating from a class reference.
Perhaps your system requires instances to be instantiated in some other way, I cannot tell from here. No matter what, however you instantiate then, you need an instance to call an instance method.
回答2:
Have you tried using an Interface Reference instead?
type
IReqBase = Interface(IInterface)
['{B71BD1C3-CE4C-438A-8090-DA6AACF0B3C4}']
procedure FillWithTemplateData;
end;
type
TWebAct = (ttlogin,
ttsignin);
TForm59 = class(TForm)
Button1: TButton;
Memo1: TMemo;
CheckBox1: TCheckBox;
Button2: TButton;
procedure Button1Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
private
{ Private declarations }
FReqList: Array of IReqBase;
procedure CreateBaseList;
procedure ClearBaseList;
public
{ Public declarations }
end;
TJSONStructure = class(TInterfacedObject);
TReqBaseClass = class of TReqBase;
TReqBase = class(TJSONStructure, IReqBase)
private
token: Int64;
protected
class function ReqClass: TReqBaseClass; virtual; abstract;
public
Constructor Create; virtual;
procedure FillWithTemplateData; virtual;
class function ReqBase: IReqBase;
end;
TReqLogin = class(TReqBase)
private
Fusername,
Fpassword: String;
Fmodule : Integer;
protected
class function ReqClass: TReqBaseClass; override;
public
Constructor Create; override;
Destructor Destroy; override;
procedure FillWithTemplateData; override;
end;
TReqSignIn = class(TReqBase)
private
Fusername,
Fpassword: String;
Fmodule : Integer;
protected
class function ReqClass: TReqBaseClass; override;
public
Constructor Create; override;
Destructor Destroy; override;
procedure FillWithTemplateData; override;
end;
var
Form59: TForm59;
implementation
{$R *.dfm}
procedure TForm59.Button1Click(Sender: TObject);
begin
Memo1.Lines.Clear;
IReqBase(FReqList[integer(CheckBox1.Checked)]).FillWithTemplateData;
end;
procedure TForm59.Button2Click(Sender: TObject);
begin
CreateBaseList;
end;
procedure TForm59.Button3Click(Sender: TObject);
begin
if CheckBox1.Checked then
TReqSignIn.ReqBase.FillWithTemplateData
else
TReqLogin.ReqBase.FillWithTemplateData;
end;
procedure TForm59.ClearBaseList;
begin
SetLength(FReqList, 0);
end;
procedure TForm59.CreateBaseList;
begin
if High(FReqList) = Ord(High(TWebAct)) +1 then
ClearBaseList;
SetLength(FReqList, Ord(High(TWebAct)) + 1 );
FReqList[ord(ttlogin)] := TReqLogin.ReqBase;
FReqList[ord(ttsignin)] := TReqSignIn.ReqBase;
end;
procedure TForm59.FormCreate(Sender: TObject);
begin
CreateBaseList;
end;
procedure TForm59.FormDestroy(Sender: TObject);
begin
ClearBaseList;
end;
{ TReqLogin }
constructor TReqLogin.Create;
begin
inherited;
FUserName := 'Rick';
FPassword := 'Test';
Fmodule := 100;
end;
destructor TReqLogin.Destroy;
begin
Form59.Memo1.Lines.Add('Destroyed: ' +ClassName);
inherited;
end;
procedure TReqLogin.FillWithTemplateData;
begin
inherited;
Form59.Memo1.Lines.Add(Fusername);
Form59.Memo1.Lines.Add(FPassword);
Form59.Memo1.Lines.Add(IntToStr(FModule));
end;
class function TReqLogin.ReqClass: TReqBaseClass;
begin
Result := TReqLogin;
end;
{ TReqBase }
constructor TReqBase.Create;
begin
inherited;
Token := -1;
end;
procedure TReqBase.FillWithTemplateData;
begin
Form59.Memo1.Lines.Add(IntToStr(Token));
end;
class function TReqBase.ReqBase: IReqBase;
begin
Result := ReqClass.Create;
end;
{ TReqSignIn }
constructor TReqSignIn.Create;
begin
inherited;
FUserName := 'Peterson';
FPassword := 'TestPW';
Fmodule := 101;
end;
destructor TReqSignIn.Destroy;
begin
Form59.Memo1.Lines.Add('Destroyed: ' +ClassName);
inherited;
end;
procedure TReqSignIn.FillWithTemplateData;
begin
inherited;
Form59.Memo1.Lines.Add(Fusername);
Form59.Memo1.Lines.Add(FPassword);
Form59.Memo1.Lines.Add(IntToStr(FModule));
end;
class function TReqSignIn.ReqClass: TReqBaseClass;
begin
Result := TReqSignIn;
end;
end.
来源:https://stackoverflow.com/questions/18125462/how-to-call-object-method-for-any-object-in-my-metaclass