Come impostare una dichiarazione anticipata con tipi generici in Delphi 2010?

Mi imbatto in quello che sembra un problema molto classico: un object e una class di raccolta, entrambi che si riferiscono l’un l’altro, che richiedono una dichiarazione in avanti. Sto usando Delphi 2010 con l’aggiornamento 5.

Funziona bene con le classi non generiche ma non riesco a risolvere l’errore E2086 con tipi generici:

type // Forward declarations TMyElement = class; // E2086: Type 'TMyElement' is not yet completely defined TMyCollection = class // end; TMyElement = class FParent: TMyCollection; end; 

Lo stesso problema si verifica quando si cambia l’ordine di dichiarazione di class.

Non ho trovato alcun riferimento a questo problema qui o in QualityCentral (sono stati trovati altri problemi con E2086, ma non relativi a questo caso d’uso)

L’unica soluzione che ho per ora è dichiarare il genitore come TObject e lanciarlo sul tipo generico dell’insieme quando richiesto (non una soluzione pulita …)

Come hai risolto questo problema o inoltrato le tue classi generiche?

Grazie,

[Modifica 22 ott 2011] Follow up su QualityCentral: ho segnalato questo bug nella centrale di qualità qui

Questo è stato chiuso di recente da EMB con il seguente stato di risoluzione: Risoluzione: come progettato Risolto nella build: 16.0.4152

Ho solo Delphi 2010. Qualcuno potrebbe confermare che è stato corretto in Delphe XE2 Update1, o significa che funziona come previsto?

[Modifica 23 ottobre 2011] Risposta finale da EMB: EMB ha confermato oggi che l’uso della dichiarazione di inoltro di un tipo generico non è supportato dal compilatore Delphi effettivo. Puoi vedere la loro risposta in QC, con il link sopra riportato.

Puoi aggirare il problema dichiarando una class di antenati:

 type TBaseElement = class end; TMyCollection = class end; TMyElement = class(TBaseElement) private FParent: TMyCollection; end; 

Sembra che Delphi si allontani dalle classi di inoltro relative ai generici.

Potresti anche pensare di creare una class TMyCollectionBase non generica spostando tutto il codice che non dipende dal tipo T, magari aumentandolo con alcune funzioni virtuali per rendere idealmente tutto ciò che è necessario quando indicato da FParent. Sto pensando in C ++ qui, ma potrebbe ridurre anche la dimensione del codice generato quando TMyCollection viene utilizzato per memorizzare elementi di diversi tipi.

Esempio di My Collection (basato su generici)

 type TMICustomItem = class(TPersistent) private FID: Variant; FCollection: TList; function GetContained: Boolean; protected procedure SetID(Value: Integer); public constructor Create(ACollection: TList); overload; constructor Create(ACollection: TList; 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 = class(TList) private function GetItemByID(ID: Integer): ItemClass; public property ItemID[ID: Integer]: ItemClass read GetItemByID; //find and return Item in self by ID end; 

 { TMICustomItem } constructor TMICustomItem.Create(ACollection: TList); begin FCollection := ACollection; end; constructor TMICustomItem.Create(ACollection: TList; 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 } function TMICustomCollection.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;