I'm trying to define my own items type for a generic list. All items need to have an owner
property.
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;
When I'm trying to change FOwner
value the compiler says that FOwner
is undeclared identifier. THandingList
inherits from TGenericCollection
. THandingItem
inherits from TGenericCollectionItem
.
What I'm doing wrong? Sample project
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.
Oh this is confusing. You've used TGenericCollectionItem
as both a generic parameter name, and the name of a type. In TGenericCollection
code the name TGenericCollectionItem
refers to the generic type parameter rather than the class of the same name. And all that you know about TGenericCollectionItem
the generic parameter is that it is a class. That is, it derives from TObject
. And so has no member named Owner
, or indeed FOwner
.
Change
TGenericCollection<TGenericCollectionItem: class>
to
TGenericCollection<T: TGenericCollectionItem>
Note that you will likely need to forward declare TGenericCollectionItem
.
Update: Argh, a forward declaration won't work when one of the mutually referential classes is generic, due to a design deficiency of Delphi generics. See How to set a forward declaration with generic types under Delphi 2010?