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?