How to set a forward declaration with generic types under Delphi 2010?
I run into what seems to be a very classical problem: An item and a collection class, both referencing each other, that require a fo开发者_JAVA技巧rward declaration. I'm using Delphi 2010 with update 5.
This works well with non generic classes but I can't workaround the E2086 error with generic types:
type
// Forward declarations
TMyElement = class; // E2086: Type 'TMyElement' is not yet completely defined
TMyCollection<T:TMyElement> = class
//
end;
TMyElement = class
FParent: TMyCollection<TMyElement>;
end;
The same issue happens when switching the class declaration order.
I didn't found any reference to this issue here or in QualityCentral (other issues with E2086 were found, but not related to this use case)
The only workaround I have for now is to declare the parent as TObject, and cast it to the collection generic type when required (not a clean solution...)
How did you workaround this issue, or forward-declare your generic classes?
Thanks,
[Edit Oct 22, 2011] Follow up on QualityCentral: I reported this bug in quality central here
This has been closed recently by EMB with the following resolution status: Resolution: As designed Resolved in build: 16.0.4152
I only have Delphi 2010. Could someone confirm that it has been fixed in Delphe XE2 Update1, or does it mean that it works 'as expected'?
[Edit Oct 23, 2011] Final answer from EMB: EMB confirmed today that using forward declaration of a generic type is not supported by the actual Delphi compiler. You can see their answer in QC, with the link provided above.
You can work around it by declaring an ancestor class:
type
TBaseElement = class
end;
TMyCollection<T: TBaseElement> = class
end;
TMyElement = class(TBaseElement)
private
FParent: TMyCollection<TBaseElement>;
end;
Looks like Delphi shies away from forwarding classes related to generics.
You may also think about creating non generic TMyCollectionBase class by moving there all code that is not dependent on the T type maybe augmenting it with some virtual functions to ideally make it all what is needed when referred by FParent. I am thinking in C++ here but it might reduce as well the size of generated code when TMyCollection is used to store items of several types.
My Collection's example (based on generics)
type
TMICustomItem = class(TPersistent)
private
FID: Variant;
FCollection: TList<TMICustomItem>;
function GetContained: Boolean;
protected
procedure SetID(Value: Integer);
public
constructor Create(ACollection: TList<TMICustomItem>); overload;
constructor Create(ACollection: TList<TMICustomItem>; ID: Integer); overload;
procedure Add; //Adding myself to parent collection
procedure Remove; //Removing myself from parent collection
property Contained: Boolean read GetContained; //Check contains myself in parent collection
property ID: Variant read FID;
end;
TMICustomCollection<ItemClass: TMICustomItem> = class(TList<ItemClass>)
private
function GetItemByID(ID: Integer): ItemClass;
public
property ItemID[ID: Integer]: ItemClass read GetItemByID; //find and return Item<ItemClass> in self by ID
end;
...
{ TMICustomItem }
constructor TMICustomItem.Create(ACollection: TList<TMICustomItem>);
begin
FCollection := ACollection;
end;
constructor TMICustomItem.Create(ACollection: TList<TMICustomItem>;
ID: Integer);
begin
Create(ACollection);
FID := ID;
end;
procedure TMICustomItem.Add;
begin
if not FCollection.Contains(Self) then
FCollection.Add(Self)
else
raise EListError.CreateRes(@SGenericDuplicateItem);
end;
procedure TMICustomItem.Remove;
begin
if FCollection.Contains(Self) then
FCollection.Remove(Self)
else
raise EListError.CreateRes(@SGenericItemNotFound);
end;
function TMICustomItem.GetContained: Boolean;
begin
Result := FCollection.Contains(Self);
end;
procedure TMICustomItem.SetID(Value: Integer);
begin
FID := Value;
end;
{ TMICustomCollection<ItemClass> }
function TMICustomCollection<ItemClass>.GetItemByID(ID: Integer): ItemClass;
var
I: Integer;
begin
for I := 0 to Count - 1 do
if Items[I].ID = ID then
Exit(Items[I]);
raise EListError.CreateRes(@SGenericItemNotFound);
end;
精彩评论