I am trying to create a generic list class for use with tiOPF (an Object Persistence Framework for delphi @ www.tiopf.com). Specifically I am trying to take an existing generic class (TtiObjectList) and make a generic version that uses TtiObject descenants.
I have limited scope for altering the base classes as they need to compile under D7 - D2009 and Free Pascal. I need to descend from TtiObjectList to keep the existing persistence mechanisms working.
// base class
type
TtiObjectList = class(TtiObject)
...
protected
function GetItems(i: integer): TtiObject; virtual;
procedure SetItems(i: integer; const AValue: TtiObject); virtual;
...
public
function Add(const AObject : TtiObject): integer; overload; virtual;
...
end;
My class is defined as follows:
TtiGenericObjectList<T: TtiObject> = class(TtiObjectList)
protected
function GetItems(i:integer): T; reintroduce;
procedure SetItems(i:integer; const Value: T); reintroduce;
public
function Add(const AObject: T): integer; reintroduce;
property Items[i:integer]: T read GetItems write SetItems; default;
end;
implementation
{ TtiGenericObjectList<T> }
function TtiGenericObjectList<T>.Add(const AObject: T): integer;
var obj: TtiObject;
begin
obj:= TtiObject(AObject); /// Invalid typecast
result:= inherited Add(obj);
end;
// alternate add, also fails
function TtiGenericObjectList<T>.Add(const AObject: T): integer;
begin
result:= inherited Add(AObject); /// **There is no overloaded version**
/// **of 'Add' that can be called with these arguments**
end;
function TtiGenericObjectList<T>.GetItems(i: integer): T;
begin
result:= T(inherited GetItems(i)); /// **Invalid typecast **
end;
procedure TtiGenericObjectList<T>.SetItems(i: integer; const Value: T);
begin
inherited SetItems(i, Value);
end;
The problem I have is that delphi is not seeing T as a TtiObject descendant. I am getting invalid typecast errors when I do something like:
function TtiGenericObjectList<T>.Add(const AObject: T): integer;
var obj: TtiObject;
begin
obj:= TtiObject(AObject); /// **Invalid typecast***
result:= inherited Add(obj);
end;
If I don't do a type cast, then I get overload errors instead as shown in the listing above.
Any ideas where I am going wrong?
Sean
The Delphi 2009 compiler has a few very serious flaws in its generics implementation. It doesn't understand the implications of constraints nearly as well as it ought to, (Barry Kelly admitted this somewhere else on SO; I don't remember exactly where,) and cross-unit generics can cause very strange problems. Best bet is to handle this one on a case-by-case basis: If your code compiles, use it. If not, go back to a non-generic implementation until they get it fixed. Hopefully we'll see an update that fixes generics (and the Generics.Collections unit) sometime in the near future.
It looks like the problem is not me, but the compiler :).
In the end, I have hacked around it using the following methods
class function TtiGenericObjectList<T>.GenericAsObject(const Value): TObject;
begin
Result := TObject(Value);
end;
class function TtiGenericObjectList<T>.ObjectAsGeneric(const Value): T;
begin
Result := T(Value);
end;
used as follows
function TtiGenericObjectList<T>.Add(const AObject: T): integer;
var obj: TtiObject;
begin
obj:= TtiObject(GenericAsObject(AObject));
result:= inherited Add(obj);
// replaces the following which gets overload errors
// result:= inherited Add(TtiObject(AObject));
end;
and
function TtiGenericObjectList<T>.GetItems(i: integer): T;
var obj: TtiObject;
begin
obj:= inherited GetItems(i);
result:= ObjectAsGeneric(obj);
// replaces the following which gets "Invalid typecast" errors
// result:= inherited Add(AObject);
end;
I will clean these up a bit and use them till the compiler gets fixed.
I recently tackled a similar problem using D2010, and here is the code I came up with.
type
TGenericList<T: TtiObject> = class(TtiObjectList)
protected
function GetItems(AIndex: integer): T; reintroduce;
procedure SetItems(AIndex: integer; const AValue: T); reintroduce;
public
property Items[i:integer]: T read GetItems write SetItems; default;
function Add(const AObject: T): integer; reintroduce;
end;
implementation
{ TGenericList<T> }
function TGenericList<T>.Add(const AObject: T): integer;
begin
Result := inherited Add(TtiObject(AObject));
end;
function TGenericList<T>.GetItems(AIndex: integer): T;
begin
Result := T(inherited GetItems(AIndex));
end;
procedure TGenericList<T>.SetItems(AIndex: integer; const AValue: T);
begin
inherited SetItems(AIndex, AValue);
end;
I have found some little issues with generics, which is not surprising for such a new feature. And you are trying to confuse the compiler real hard ;-).
Unfortunately I have no 2009 at hand here so I can't test, but I have some suggestions:
Have you looked for updates (and installed them)?
Have you tried using the as and is operators:
obj:= AObject as TtiObject;
Have you tried using an intermediate class:
TtiGenericList<T: TObject> = class(TtiObjectList)
protected
function GetItems(i:integer): T; reintroduce;
procedure SetItems(i:integer; const Value: T); reintroduce;
public
function Add(const AObject: T): integer; reintroduce;
property Items[i:integer]: T read GetItems write SetItems; default;
end;
(I got here due to a similar problem, but while nailing it down I got to a different solution, though with a newer(XE) Delphi):
Or simply try to make that local variable Obj of type T.
来源:https://stackoverflow.com/questions/580108/delphi-generic-constraints-problem