Как установить форвардное объявление с универсальными типами в Delphi 2010?

Я столкнулся с очень классической проблемой: элемент и класс коллекции, которые ссылаются друг на друга, требуют предварительного объявления. Я использую 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 по ссылке, указанной выше.


person user315561    schedule 19.05.2011    source источник
comment
+1 это действительно должно работать ИМХО   -  person jpfollenius    schedule 19.05.2011
comment
Эта ошибка до сих пор не исправлена ​​в XE6.   -  person kot-da-vinci    schedule 25.08.2014
comment
@kot нет ошибки, которую нужно исправить   -  person David Heffernan    schedule 04.08.2015


Ответы (3)


Вы можете обойти это, объявив класс-предок:

type
  TBaseElement = class
  end;

  TMyCollection<T: TBaseElement> = class
  end;

  TMyElement = class(TBaseElement)
  private
    FParent: TMyCollection<TBaseElement>;
  end;
person Ondrej Kelle    schedule 19.05.2011
comment
Добро пожаловать, я рада, что это вам помогло. :-) - person Ondrej Kelle; 19.05.2011
comment
После некоторого тестирования базирование TMyCollection на TBaseElement мало помогает, поскольку MyCollection.T не предоставляет доступа к методам и свойствам реального класса TMyelement. У вас была другая идея, лежащая в основе этого решения? - person user315561; 19.05.2011
comment
TBaseElement может, например, объявлять некоторые абстрактные методы, реализованные в TMyElement, но все еще доступные через полиморфизм через класс TBaseElement. - person Ondrej Kelle; 19.05.2011

Похоже, Delphi уклоняется от перенаправления классов, связанных с дженериками.

Вы также можете подумать о создании не общего класса TMyCollectionBase, переместив туда весь код, не зависящий от типа T, возможно, дополнив его некоторыми виртуальными функциями, чтобы в идеале сделать все то, что необходимо при обращении FParent. Я думаю здесь о C ++, но это может также уменьшить размер сгенерированного кода, когда TMyCollection используется для хранения элементов нескольких типов.

person jszpilewski    schedule 19.05.2011
comment
удаление всего кода, не зависящего от параметра типа, не оставит ничего полезного! - person David Heffernan; 19.05.2011
comment
Есть некоторые функции, такие как count (), по крайней мере, его подпись не зависит от базового типа элемента. - person jszpilewski; 19.05.2011

Пример моей коллекции (на основе дженериков)

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;
person Komarov Andrey    schedule 26.08.2015