How to define a template for generic collection item?

I'm trying to define my own items type for a generic list. All items need to have an owner имущество.

type 
  TGenericCollection<TGenericCollectionItem: class> = class(TObjectList<TGenericCollectionItem>)
  protected
    procedure Notify(const Value: TGenericCollectionItem; Action: TCollectionNotification); override;
  public
    procedure Assign(Source: TGenericCollection<TGenericCollectionItem>); virtual;
  end;

  TGenericCollectionItem = class
  private
    FOwner: TGenericCollection<TGenericCollectionItem>;  //another class can change this value if it was declared in the same unit
  protected
    function GetOwner: TGenericCollection<TGenericCollectionItem>; virtual;
  public
    constructor Create; virtual;
    property Owner: TGenericCollection<TGenericCollectionItem> read GetOwner;
  end;

implementation

{ TGenericCollection<TGenericCollectionItem> }
procedure TGenericCollection<TGenericCollectionItem>.Assign(
  Source: TGenericCollection<TGenericCollectionItem>);
begin
  AddRange(Source.ToArray);
end;

procedure TGenericCollection<TGenericCollectionItem>.Notify(
  const Value: TGenericCollectionItem; Action: TCollectionNotification);
begin
  inherited;

  if (Action = cnAdded) then
  begin
    OutputDebugString(PChar(Value.ClassName)); // Debug Output: THandingItem Process GenericCollection.exe (2424)
    OutputDebugString(PChar(Self.ClassName));  // Debug Output: THandingList Process GenericCollection.exe (2424)
//    Value.FOwner := Self;  // Error: [dcc32 Error] Unit2.pas(46): E2003 Undeclared identifier: 'FOwner'
//    (Value as TGenericCollectionItem).FOwner := Self;   // Error: [dcc32 Error] Unit2.pas(46): E2003 Undeclared identifier: 'FOwner'
//    (Value as TGenericCollectionItem<T>).FOwner := Self;   // Undeclared identifier 'T'
  end;
end;

Когда я пытаюсь изменить FOwner value the compiler says that FOwner is undeclared identifier. THandingList наследуется от TGenericCollection, THandingItem наследуется от TGenericCollectionItem,

Что я делаю не так? Пример проекта


UPD: TGenericCollectionItem changed in code sample.


Console app:

program Project1;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils,
  System.Generics.Collections;

type
  TGenericCollection<TGenericCollectionItem: class> = class(TObjectList<TGenericCollectionItem>)
  protected
    procedure Notify(const Value: TGenericCollectionItem; Action: TCollectionNotification); override;
  end;

  TGenericCollectionItem = class
  public
    Owner: TGenericCollection<TGenericCollectionItem>;
  end;

  procedure TGenericCollection<TGenericCollectionItem>.Notify(
    const Value: TGenericCollectionItem; Action: TCollectionNotification);
  begin
    inherited;

    if (Action = cnAdded) then
      Value.Owner := Self;
  end;

var
  GenericCollectionItem: TGenericCollectionItem;
  GenericCollection: TGenericCollection<TGenericCollectionItem>;
begin
  try
    { TODO -oUser -cConsole Main : Insert code here }
    GenericCollection := TGenericCollection<TGenericCollectionItem>.Create;
    try
      GenericCollectionItem := TGenericCollectionItem.Create;
      GenericCollection.Add(GenericCollectionItem);
    finally
      GenericCollection.Free;
    end;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.

1 ответ

Решение

Ох, это сбивает с толку. Вы использовали TGenericCollectionItem как имя общего параметра, так и имя типа. В TGenericCollection закодировать имя TGenericCollectionItem относится к параметру универсального типа, а не к классу с тем же именем. И все, что вы знаете о TGenericCollectionItem универсальным параметром является то, что это класс. То есть это происходит от TObject, И поэтому не имеет ни одного члена по имени Ownerили действительно FOwner,

+ Изменить

TGenericCollection<TGenericCollectionItem: class>

в

TGenericCollection<T: TGenericCollectionItem>

Обратите внимание, что вам, вероятно, нужно будет отправить декларацию TGenericCollectionItem,

Обновление: Argh, предварительное объявление не будет работать, когда один из взаимно-ссылочных классов является универсальным, из-за недостатка дизайна в Delphi. См. Как установить предварительное объявление с универсальными типами в Delphi 2010?

Другие вопросы по тегам