Search code examples
delphigenericscollectionsdelphi-xe6

How to implement TCollection and TCollectionItem functionality in generics


I'm trying to implement TCollection and TCollectionItem functionality in generics. To solve this problem we need to make a forward declaration of TGenericCollectionItem or TGenericCollection. Using forward declaration of a generic type is not supported by the actual (XE6) Delphi compiler. There is a hack how to make it. But we still have a problem with assigning an Owner to TCollectionItem.

  TBaseElement = class // hack for forward declaration of a generic type 
  end;

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

  TGenericCollectionItem = class(TBaseElement)
  public
    Owner: TGenericCollection<TBaseElement>;
  end;

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

    if (Action = cnAdded) then
    begin
      if (Value is TGenericCollectionItem) then
        (Value as TGenericCollectionItem).Owner := Self;  //here is error
    end;
  end;

E2010 Incompatible types: 'TGenericCollection<TBaseElement>' and 'TGenericCollection<TGenericCollection<T>.T>'

How to solve this conflict?


Solution

  • What you are looking for is this code:

    unit Generics.Legacy;
    
    
    {$WARN SYMBOL_DEPRECATED OFF} // we know that Added and Deleting are deprecated
    {$HINTS OFF} // we know we have intentionally hidden some overridden methods
    
    
    interface
    
    
    uses
      Classes;
    
    
    type
      TCollection<T: TCollectionItem> = class(TCollection)
      private
        procedure Added(var Item: TCollectionItem); overload; override;
        procedure Deleting(Item: TCollectionItem); overload; override;
        procedure Notify(Item: TCollectionItem; Action: TCollectionNotification); overload; override;
        procedure SetItemName(Item: TCollectionItem); overload; override;
        procedure Update(Item: TCollectionItem); overload; override;
      protected
        procedure Added(var Item: T); reintroduce; overload; virtual; deprecated;
        procedure Deleting(Item: T); reintroduce; overload; virtual; deprecated;
        function GetItem(Index: Integer): T;
        procedure Notify(Item: T; Action: TCollectionNotification); reintroduce;
    overload; virtual;
        procedure SetItem(Index: Integer; Value: T);
        procedure SetItemName(Item: T); reintroduce; overload; virtual;
        procedure Update(Item: T); reintroduce; overload; virtual;
      public
        type
          TCollectionEnumerator = class(Classes.TCollectionEnumerator)
          public
            function GetCurrent: T; inline;
            property Current: T read GetCurrent;
          end;
    
    
        constructor Create;
        function Add: T;
        function FindItemID(ID: Integer): T;
        function Insert(Index: Integer): T;
        function GetEnumerator: TCollection<T>.TCollectionEnumerator;
        property Items[Index: Integer]: T read GetItem write SetItem;
      end;
    
    
      TOwnedCollection<T: TCollectionItem> = class(TCollection<T>)
      private
        FOwner: TPersistent;
      protected
        function GetOwner: TPersistent; override;
      public
        constructor Create(AOwner: TPersistent);
      end;
    
    
    implementation
    
    
    { TCollection<T> }
    constructor TCollection<T>.Create;
    begin
      inherited Create(T);
    end;
    
    
    function TCollection<T>.Add: T;
    begin
      Result := T(inherited Add);
    end;
    
    
    procedure TCollection<T>.Added(var Item: T);
    begin
    end;
    
    
    procedure TCollection<T>.Added(var Item: TCollectionItem);
    begin
      Added(T(Item));
    end;
    
    
    procedure TCollection<T>.Deleting(Item: T);
    begin
    end;
    
    
    procedure TCollection<T>.Deleting(Item: TCollectionItem);
    begin
      Deleting(T(Item));
    end;
    
    
    function TCollection<T>.FindItemID(ID: Integer): T;
    begin
      Result := T(inherited FindItemID(ID));
    end;
    
    
    function TCollection<T>.GetEnumerator: TCollection<T>.TCollectionEnumerator;
    begin
      Result := TCollection<T>.TCollectionEnumerator.Create(Self);
    end;
    
    
    function TCollection<T>.GetItem(Index: Integer): T;
    begin
      Result := T(inherited GetItem(Index));
    end;
    
    
    function TCollection<T>.Insert(Index: Integer): T;
    begin
      Result := T(inherited Insert(Index));
    end;
    
    
    procedure TCollection<T>.Notify(Item: T; Action: TCollectionNotification);
    begin
      inherited Notify(Item, Action);
    end;
    
    
    procedure TCollection<T>.Notify(Item: TCollectionItem; Action: TCollectionNotification);
    begin
      Notify(T(Item), Action);
    end;
    
    
    procedure TCollection<T>.SetItem(Index: Integer; Value: T);
    begin
      inherited SetItem(Index, Value);
    end;
    
    
    procedure TCollection<T>.SetItemName(Item: T);
    begin
      inherited SetItemName(Item);
    end;
    
    
    procedure TCollection<T>.SetItemName(Item: TCollectionItem);
    begin
      SetItemName(T(Item));
    end;
    
    
    procedure TCollection<T>.Update(Item: T);
    begin
      inherited Update(Item);
    end;
    
    
    procedure TCollection<T>.Update(Item: TCollectionItem);
    begin
      Update(T(Item));
    end;
    
    
    { TCollection<T>.TCollectionEnumerator }
    function TCollection<T>.TCollectionEnumerator.GetCurrent: T;
    begin
      Result := T(inherited GetCurrent);
    end;
    
    
    { TOwnedCollection<T> }
    constructor TOwnedCollection<T>.Create(AOwner: TPersistent);
    begin
      FOwner := AOwner;
      inherited Create(T);
    end;
    
    
    function TOwnedCollection<T>.GetOwner: TPersistent;
    begin
      Result := FOwner;
    end;
    
    
    end.
    

    I don't know the original author of that code (not me), but I think it was posted some time ago at the Embarcadero forums. A similar implementation can be found in DSharp.Core.Collections