Object Pascal 从基础 class 调用或调用后代的方法
Object Pascal call or invoke descendant's method from base class
我想到了这个对象:
TBaseObject = class
private
FEditState: string;
FID: integer;
public
constructor Create;
...
procedure Clone(AObject: TObject); virtual; //I actually want AObject to be generic
property EditState: string read FEditState write FEditState;
property ID: integer read FID write FID;
end;
constructor TBaseObject.Create;
begin
FEditState := 'none';
end;
这是一个后代class:
TUser = class(TBaseObject)
private
FUsername: string;
public
procedure Clone(AObject: TObject); override;
property Username: string read FUsername write FUsername;
...
end;
...
procedure TUser.Clone(AObject: TObject);
begin
self.id := aobject.id;
...
end;
然后我做了一个容器对象如下:
TBaseObjects<T:class> = class
private
FItems: TObjectList<T>;
FDeletedItems: TObjectList<T>;
function GetItem(Index: Integer): T;
public
function Add(NewItem: T=Default(T)): T; // adds to FItems
function DeleteItem(AObject: T): T; // save to FDeletedItems, delete from FItems
property Items[Index: Integer]: T read GetItem; default;
...
function TBaseObjects<T>.DeleteItem(AObject: T): T;
begin
result := T.Create;
result.Clone(AObject); // ERROR: no member Clone...
FItems.Remove(...);
end;
用作:
TUsers = TBaseBOMList<TUser>;
var
Users: TUsers;
可以看出,我尝试使用后代的 clone
方法将要删除的项目的副本保存到 FDeletedItems 通用列表中,然后从 FItems 中删除,但失败了。编译器说 'no member Clone'.
如果我正在做的事情无法完成,应该如何处理?
根据 Dalija 的建议,我声明了 TBaseObjects<T:TBaseObject>
而不是 TBaseObjects<T:class>
。
任何好奇或感兴趣的人都可以在下面找到完整的测试程序。
此外,如果有人可以使用纯多态性而不是 DelphiCoder 暗示的泛型更有效地做到这一点,我很乐意重新考虑,因为现在,如果没有泛型,我将不得不声明和定义一个 TBaseBOMList并为我想使用的每个基础对象(TUser、TRole 等)复制每个方法。
代码:
program ProjTestGenerics;
{$mode delphi}
uses
sysutils, TypInfo, generics.Collections;
type
{ TBaseBOM }
TBaseBOM = class
private
FEditState: string;
FID: integer;
public
constructor Create;
procedure Assign(src: TBaseBOM);
published
property EditState: string read FEditState write FEditState;
property ID: integer read FID write FID;
end;
{ TBaseBOMList }
TBaseBOMList<T:TBaseBOM> = class
private
FItems: TObjectList<T>;
FDeletedItems: TObjectList<T>;
function GetItem(Index: Integer): T;
public
constructor Create;
destructor Destroy; override;
function Add(NewItem: T=Default(T)): T;
function Delete(Index: Integer): Boolean;
function Find(APropertyName: string; const AValue: variant): Integer;
property Items[Index: Integer]: T read GetItem; default;
end;
{ TRole }
TRole = class(TBaseBOM)
private
FRolename: string;
public
procedure Assign( AObject: TBaseBOM );
published
property Rolename: string read FRolename write FRolename;
end;
{ TUser }
TUser = class(TBaseBOM)
private
FUsername: string;
public
procedure Assign( AObject: TBaseBOM );
published
property Username: string read FUsername write FUsername;
end;
{ TUserRole }
TUserRole = class(TBaseBOM)
private
FRolename: string;
FUsername: string;
public
procedure Assign( AObject: TBaseBOM );
published
property Username: string read FUsername write FUsername;
property Rolename: string read FRolename write FRolename;
end;
TUsers = TBaseBOMList<TUser>;
TRoles = TBaseBOMList<TRole>;
TUserRoles = TBaseBOMList<TUserRole>;
function TBaseBOMList<T>.GetItem(Index: Integer): T;
begin
result := FItems[Index];
end;
constructor TBaseBOMList<T>.Create;
begin
inherited Create;
FItems := TObjectList<T>.Create(true);
FDeletedItems := TObjectList<T>.Create(true);
end;
destructor TBaseBOMList<T>.Destroy;
begin
FDeletedItems.Free;
FItems.Free;
inherited Destroy;
end;
function TBaseBOMList<T>.Add(NewItem: T): T;
begin
if NewItem = Default(T) then
result := T.Create
else
result := NewItem;
FItems.Add(result);
end;
function TBaseBOMList<T>.Delete(Index: Integer): Boolean;
var
o: T;
begin
o := T.Create;
o.Assign(FItems[Index]);
FDeletedItems.Add(o);
FItems.Delete(Index); // error if index not valid
result := true;
end;
function TBaseBOMList<T>.Find(APropertyName: string; const AValue: variant
): Integer;
var
value : Variant;
PropList: PPropList;
PropCount, i: integer;
PropExist: Boolean;
begin
Result := -1;
PropExist:= False;
PropCount := GetPropList(T, PropList);
try
for i := 0 to PropCount-1 do
if CompareText(PropList[i].Name, APropertyName) = 0 then
begin
PropExist := True;
break;
end;
finally
Freemem(PropList);
end;
if PropExist then
begin
for i := 0 to FItems.Count-1 do
begin
value := GetStrProp(FItems[i], APropertyName);
if value = AValue then
begin
Result := i;
end;
end;
end
else
Raise Exception.Create(Format('Property name ''%s'' not found.',[APropertyName]));
end;
procedure TUserRole.Assign(AObject: TBaseBOM);
begin
inherited Assign(AObject);
with TUserRole(AObject) do
begin
self.Rolename:= Rolename;
self.Username:= Username;
end;
end;
procedure TRole.Assign(AObject: TBaseBOM);
begin
with TRole(AObject) do
self.Rolename:= Rolename;
end;
procedure TUser.Assign(AObject: TBaseBOM);
begin
with TUser(AObject) do
self.Username:= Username;
end;
{ TBaseBOM }
constructor TBaseBOM.Create;
begin
FEditState:= 'none';
end;
procedure TBaseBOM.Assign(src: TBaseBOM);
begin
with src do
begin
self.ID:= src.ID;
self.EditState:= src.EditState;
end;
end;
var
users: TUsers;
roles: TRoles;
u: TUser;
r: TRole;
urs: TUserRoles;
ur: TUserRole;
i: Integer;
begin
roles := TRoles.Create;
r := TRole.Create;
r.Rolename:= 'admin';
roles.Add(r);
r := roles.Add;
r.rolename := 'processor';
users := TUsers.Create;
u := TUser.Create;
u.Username:= 'magic';
users.Add(u);
urs := TUserRoles.Create;
ur := TUserRole.Create;
ur.ID:= 999;
ur.Username:= 'magic';
ur.Rolename:= 'processor';
urs.Add(ur);
writeln('Find username magic');
i := users.Find('username', 'magic');
writeln(users[i].username);
writeln('Find role ''processor''');
i := roles.Find('rolename', 'processor');
writeln(roles[i].rolename);
writeln('Delete last found role');
roles.Delete(i);
writeln('Deleted roles:');
writeln(roles.FDeletedItems[0].Rolename);
writeln('Find rolename ''processor'' in user roles');
i := urs.Find('rolename', 'processor');
writeln(urs[i].Rolename, ' / ', urs[i].Username);
writeln('Delete rolename ''processor'' in user roles');
urs.Delete(i);
writeln(urs.FDeletedItems[0].Rolename, ' / ', urs.FDeletedItems[0].Username);
writeln(urs.FDeletedItems[0].ID, ' / ', urs.FDeletedItems[0].EditState);
urs.free;
users.free;
roles.free;
writeln('ok');
readln();
end.
我想到了这个对象:
TBaseObject = class
private
FEditState: string;
FID: integer;
public
constructor Create;
...
procedure Clone(AObject: TObject); virtual; //I actually want AObject to be generic
property EditState: string read FEditState write FEditState;
property ID: integer read FID write FID;
end;
constructor TBaseObject.Create;
begin
FEditState := 'none';
end;
这是一个后代class:
TUser = class(TBaseObject)
private
FUsername: string;
public
procedure Clone(AObject: TObject); override;
property Username: string read FUsername write FUsername;
...
end;
...
procedure TUser.Clone(AObject: TObject);
begin
self.id := aobject.id;
...
end;
然后我做了一个容器对象如下:
TBaseObjects<T:class> = class
private
FItems: TObjectList<T>;
FDeletedItems: TObjectList<T>;
function GetItem(Index: Integer): T;
public
function Add(NewItem: T=Default(T)): T; // adds to FItems
function DeleteItem(AObject: T): T; // save to FDeletedItems, delete from FItems
property Items[Index: Integer]: T read GetItem; default;
...
function TBaseObjects<T>.DeleteItem(AObject: T): T;
begin
result := T.Create;
result.Clone(AObject); // ERROR: no member Clone...
FItems.Remove(...);
end;
用作:
TUsers = TBaseBOMList<TUser>;
var
Users: TUsers;
可以看出,我尝试使用后代的 clone
方法将要删除的项目的副本保存到 FDeletedItems 通用列表中,然后从 FItems 中删除,但失败了。编译器说 'no member Clone'.
如果我正在做的事情无法完成,应该如何处理?
根据 Dalija 的建议,我声明了 TBaseObjects<T:TBaseObject>
而不是 TBaseObjects<T:class>
。
任何好奇或感兴趣的人都可以在下面找到完整的测试程序。
此外,如果有人可以使用纯多态性而不是 DelphiCoder 暗示的泛型更有效地做到这一点,我很乐意重新考虑,因为现在,如果没有泛型,我将不得不声明和定义一个 TBaseBOMList并为我想使用的每个基础对象(TUser、TRole 等)复制每个方法。
代码:
program ProjTestGenerics;
{$mode delphi}
uses
sysutils, TypInfo, generics.Collections;
type
{ TBaseBOM }
TBaseBOM = class
private
FEditState: string;
FID: integer;
public
constructor Create;
procedure Assign(src: TBaseBOM);
published
property EditState: string read FEditState write FEditState;
property ID: integer read FID write FID;
end;
{ TBaseBOMList }
TBaseBOMList<T:TBaseBOM> = class
private
FItems: TObjectList<T>;
FDeletedItems: TObjectList<T>;
function GetItem(Index: Integer): T;
public
constructor Create;
destructor Destroy; override;
function Add(NewItem: T=Default(T)): T;
function Delete(Index: Integer): Boolean;
function Find(APropertyName: string; const AValue: variant): Integer;
property Items[Index: Integer]: T read GetItem; default;
end;
{ TRole }
TRole = class(TBaseBOM)
private
FRolename: string;
public
procedure Assign( AObject: TBaseBOM );
published
property Rolename: string read FRolename write FRolename;
end;
{ TUser }
TUser = class(TBaseBOM)
private
FUsername: string;
public
procedure Assign( AObject: TBaseBOM );
published
property Username: string read FUsername write FUsername;
end;
{ TUserRole }
TUserRole = class(TBaseBOM)
private
FRolename: string;
FUsername: string;
public
procedure Assign( AObject: TBaseBOM );
published
property Username: string read FUsername write FUsername;
property Rolename: string read FRolename write FRolename;
end;
TUsers = TBaseBOMList<TUser>;
TRoles = TBaseBOMList<TRole>;
TUserRoles = TBaseBOMList<TUserRole>;
function TBaseBOMList<T>.GetItem(Index: Integer): T;
begin
result := FItems[Index];
end;
constructor TBaseBOMList<T>.Create;
begin
inherited Create;
FItems := TObjectList<T>.Create(true);
FDeletedItems := TObjectList<T>.Create(true);
end;
destructor TBaseBOMList<T>.Destroy;
begin
FDeletedItems.Free;
FItems.Free;
inherited Destroy;
end;
function TBaseBOMList<T>.Add(NewItem: T): T;
begin
if NewItem = Default(T) then
result := T.Create
else
result := NewItem;
FItems.Add(result);
end;
function TBaseBOMList<T>.Delete(Index: Integer): Boolean;
var
o: T;
begin
o := T.Create;
o.Assign(FItems[Index]);
FDeletedItems.Add(o);
FItems.Delete(Index); // error if index not valid
result := true;
end;
function TBaseBOMList<T>.Find(APropertyName: string; const AValue: variant
): Integer;
var
value : Variant;
PropList: PPropList;
PropCount, i: integer;
PropExist: Boolean;
begin
Result := -1;
PropExist:= False;
PropCount := GetPropList(T, PropList);
try
for i := 0 to PropCount-1 do
if CompareText(PropList[i].Name, APropertyName) = 0 then
begin
PropExist := True;
break;
end;
finally
Freemem(PropList);
end;
if PropExist then
begin
for i := 0 to FItems.Count-1 do
begin
value := GetStrProp(FItems[i], APropertyName);
if value = AValue then
begin
Result := i;
end;
end;
end
else
Raise Exception.Create(Format('Property name ''%s'' not found.',[APropertyName]));
end;
procedure TUserRole.Assign(AObject: TBaseBOM);
begin
inherited Assign(AObject);
with TUserRole(AObject) do
begin
self.Rolename:= Rolename;
self.Username:= Username;
end;
end;
procedure TRole.Assign(AObject: TBaseBOM);
begin
with TRole(AObject) do
self.Rolename:= Rolename;
end;
procedure TUser.Assign(AObject: TBaseBOM);
begin
with TUser(AObject) do
self.Username:= Username;
end;
{ TBaseBOM }
constructor TBaseBOM.Create;
begin
FEditState:= 'none';
end;
procedure TBaseBOM.Assign(src: TBaseBOM);
begin
with src do
begin
self.ID:= src.ID;
self.EditState:= src.EditState;
end;
end;
var
users: TUsers;
roles: TRoles;
u: TUser;
r: TRole;
urs: TUserRoles;
ur: TUserRole;
i: Integer;
begin
roles := TRoles.Create;
r := TRole.Create;
r.Rolename:= 'admin';
roles.Add(r);
r := roles.Add;
r.rolename := 'processor';
users := TUsers.Create;
u := TUser.Create;
u.Username:= 'magic';
users.Add(u);
urs := TUserRoles.Create;
ur := TUserRole.Create;
ur.ID:= 999;
ur.Username:= 'magic';
ur.Rolename:= 'processor';
urs.Add(ur);
writeln('Find username magic');
i := users.Find('username', 'magic');
writeln(users[i].username);
writeln('Find role ''processor''');
i := roles.Find('rolename', 'processor');
writeln(roles[i].rolename);
writeln('Delete last found role');
roles.Delete(i);
writeln('Deleted roles:');
writeln(roles.FDeletedItems[0].Rolename);
writeln('Find rolename ''processor'' in user roles');
i := urs.Find('rolename', 'processor');
writeln(urs[i].Rolename, ' / ', urs[i].Username);
writeln('Delete rolename ''processor'' in user roles');
urs.Delete(i);
writeln(urs.FDeletedItems[0].Rolename, ' / ', urs.FDeletedItems[0].Username);
writeln(urs.FDeletedItems[0].ID, ' / ', urs.FDeletedItems[0].EditState);
urs.free;
users.free;
roles.free;
writeln('ok');
readln();
end.