Ответ 1
Вы можете обойти это, объявив класс предка:
type
TBaseElement = class
end;
TMyCollection<T: TBaseElement> = class
end;
TMyElement = class(TBaseElement)
private
FParent: TMyCollection<TBaseElement>;
end;
Я сталкиваюсь с тем, что кажется очень классической проблемой: элемент и класс коллекции, как ссылки друг на друга, требующие прямого объявления. Я использую Delphi 2010 с обновлением 5.
Это хорошо работает с не-универсальными классами, но я не могу обойти ошибку E2086 с общими типами:
type
// Forward declarations
TMyElement = class; // E2086: Type 'TMyElement' is not yet completely defined
TMyCollection<T:TMyElement> = class
//
end;
TMyElement = class
FParent: TMyCollection<TMyElement>;
end;
Такая же проблема возникает при переключении порядка объявления класса.
Я не нашел ссылки на эту проблему здесь или в QualityCentral (другие проблемы с E2086 были найдены, но не связаны с этим прецедентом)
Единственным обходным решением, которое у меня есть сейчас, является объявление родителя как TObject и приведение его в общий тип коллекции при необходимости (не чистое решение...)
Как вы устранили эту проблему или перешлите объявление своих общих классов?
Спасибо,
[Редактировать 22 октября 2011] Последующие действия над QualityCentral: Я сообщил об этой ошибке в качестве центрального здесь
Это недавно было закрыто EMB со следующим разрешением: Разрешение: согласно Решено в сборке: 16.0.4152
У меня только Delphi 2010. Может ли кто-нибудь подтвердить, что он был исправлен в Delphe XE2 Update1, или это означает, что он работает "как ожидалось"?
[Редактировать 23 октября 2011] Окончательный ответ от EMB: EMB подтвердил сегодня, что использование прямого объявления общего типа не поддерживается фактическим компилятором Delphi. Вы можете увидеть их ответ в QC, с приведенной выше ссылкой.
Вы можете обойти это, объявив класс предка:
type
TBaseElement = class
end;
TMyCollection<T: TBaseElement> = class
end;
TMyElement = class(TBaseElement)
private
FParent: TMyCollection<TBaseElement>;
end;
Похоже, что Delphi уклоняется от классов пересылки, связанных с дженериками.
Вы также можете подумать о создании не общего класса TMyCollectionBase, переместив туда весь код, который не зависит от типа T, возможно, увеличив его с помощью некоторых виртуальных функций, чтобы в идеале сделать все, что нужно, когда ссылается на FParent. Я думаю в С++ здесь, но это может уменьшить размер сгенерированного кода, когда TMyCollection используется для хранения элементов нескольких типов.
Пример моей коллекции (на основе дженериков)
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;