如何使用可覆盖的自有项目制作基本的通用可覆盖拥有集合?

时间:2016-02-28 08:55:18

标签: delphi generics collections

在Delphi 10 Seattle中,我想制作具有如下结构的TProject类:

  // TProject
  //   Frames[]:TFrame   -- all frames in project folder
  //   Sounds[]:TSound   -- all sounds in project folder
  //   WorkSets[]:TWorkSet -- frames subsets for some kind of works
  //     WorkSetFrames[]:TWorkSetFrame
  //   Clips[]:TClip       -- recorded clips
  //     ClipFrames[]:TClipFrame

我想要的不仅仅是从项目到集合以及从集合到项目的前向链接。此外,我希望从每个最终项目到其拥有的集合以及从每个集合到其所有者对象的反向链接。

主要请求 - 必须正确输入所有链接:

  • TProject.Clips必须是TClipCollection类型,派生自该基本泛型集合类;
  • TClip也必须从该基本通用集合项类派生。
  • TClipCollection.Owner必须是TProject
  • TClip.OwnerCollection必须是TClipCollection

它必须由构造函数填充并从该基本泛型类中获取。

接下来 - 它必须是可覆盖的,允许我将属性Name添加到泛型集合项后代和FindByName函数到泛型集合类,并获得从这两个新泛型类实例化我的一些集合的可能性。例如,使用此功能制作TFrame和TFrameCollection。

它可以基于TCollection或TList,但它们都没有所有必需的功能。

我尝试了一些声明,但是导致了类不兼容错误(解释中提到了https://en.wikipedia.org/wiki/Covariance_and_contravariance_%28computer_science%29问题)。

UPD 1:

最后一次尝试解决使用泛型集合和泛型集合项的问题,它们互相使用并允许覆盖它,这使我得到了这段代码:

unit GenericCollection;

interface
uses Generics.Collections;

type
  TGenericCollectionItem<TCollectionOwner: class> = class
  public
    type
      TCollection = class(TList<TGenericCollectionItem<TCollectionOwner>>)
      private
        FOwner: TCollectionOwner;
      public
        property Owner: TCollectionOwner read FOwner;
        constructor Create(AOwner: TCollectionOwner);
      end;

  private
    FOwnerCollection: TCollection;
    function GetIndex: Integer;
    procedure SetIndex(const Value: Integer);
  public
    property Index: Integer read GetIndex write SetIndex;
    property OwnerCollection: TCollection read FOwnerCollection;
  end;

  TNamedGenericCollectionItem<TCollectionOwner: class> = class(TGenericCollectionItem<TCollectionOwner>)
  public
    type
      TNamedGenericCollection = class(TCollection)
      public
        function FindItemName(AName: string): TNamedGenericCollectionItem<TCollectionOwner>;
      end;
  private
    FName: string;
  public
    property Name: string read FName write FName;
  end;

implementation

{ TGenericCollectionItem<ACollectionOwnerType> }

function TGenericCollectionItem<TCollectionOwner>.GetIndex: Integer;
begin
  Result := OwnerCollection.IndexOf(Self);
end;

procedure TGenericCollectionItem<TCollectionOwner>.SetIndex(
  const Value: Integer);
var
  CurIndex: Integer;
begin
  CurIndex := GetIndex;
  if (CurIndex >= 0) and (CurIndex <> Value) then
    FOwnerCollection.Move(CurIndex, Value);
end;

{ TGenericCollectionItem<ACollectionOwnerType>.TCollection }

constructor TGenericCollectionItem<TCollectionOwner>.TCollection.Create(
  AOwner: TCollectionOwner);
begin
  inherited Create;
  FOwner := AOwner;
end;

{ TNamedGenericCollectionItem<TCollectionOwner>.TNamedGenericCollection }

function TNamedGenericCollectionItem<TCollectionOwner>.TNamedGenericCollection.FindItemName(
  AName: string): TNamedGenericCollectionItem<TCollectionOwner>;
var
  X: TGenericCollectionItem<TCollectionOwner>;
begin
  // TODO: Use hash-based index
  for X in Self do
    if TNamedGenericCollectionItem<TCollectionOwner>(X).Name = AName then
      Exit(TNamedGenericCollectionItem<TCollectionOwner>(X));
end;

end.

但是当我通过声明

来使用它时
  TFrame = class(TNamedGenericCollectionItem<TProject>)
  end;

并添加到TProject

FFrames: TFrame.TNamedGenericCollection;

TProject构造函数中的这个调用

FFrames := TFrame.TNamedGenericCollection.Create(Self);

仍然令我恼火的例外:

[dcc32 Error] ProjectInfo.pas(109): E2010 Incompatible types:
'TNamedGenericCollectionItem<TCollectionOwner>.TNamedGenericCollection' and
'GenericCollection.TNamedGenericCollectionItem<ProjectInfo.TProject>.TNamedGenericCollection'

我能做些什么来解决这个问题?

1 个答案:

答案 0 :(得分:1)

我真的认为你过分思考这一点。一旦我了解了你想要做的事情,你只需要拥有者知道它的项目是什么(例如,TObjectList&lt; T&gt; T已经知道),并且项目知道它的拥有者是什么,这是容易建造。

#div-to-contain-image img {
    display: block;
    height: 100%;
    width: 100%;
}

要将其扩展到后代,如果所有者没有更改,这对于项目来说就足够了。但确实如此。但是,我们需要做的就是使用泛型来向后代反映所有者如何更改 - 比如

unit Unitgenerics;

interface

uses
  System.Generics.Collections;

type
  TGItem<TOwner : class> = class
  private
    fOwner: TOwner;
  public
    constructor Create( const pOwner : TOwner );
    property Owner : TOwner
             read fOwner;
  end;

  TRealItem = class;

  TRealOwner = class( TObjectList<TRealItem> )
    // Items are already of type TRealItem
  end;

  TRealItem = class(TGItem< TRealOwner > )
    // Owner already defined and is of type TRealOwner
  end;

implementation

{ TGItem<TOwner> }

constructor TGItem<TOwner>.Create(const pOwner: TOwner);
begin
  inherited Create;
  fOwner := pOwner;
end;

end.

这是如何在没有嵌套泛型的情况下进一步扩展......

unit Unitgenerics;

interface

uses
  System.Generics.Collections;

type
  TGItem<TOwner : class> = class
  private
    fOwner: TOwner;
  public
    constructor Create( const pOwner : TOwner );
    property Owner : TOwner
             read fOwner;
  end;

  TRealItem< TOwner : class > = class;

  TRealOwner<TOwner : class> = class( TObjectList<TRealItem< TOwner >> )
    // Items are already of type TRealItem<TOwner>
  end;

  TRealItem< TOwner : class > = class(TGItem< TRealOwner<TOwner> > )
    // Owner already defined and is of type TRealOwner<TOwner>
  end;

implementation

{ TGItem<TOwner> }

constructor TGItem<TOwner>.Create(const pOwner: TOwner);
begin
  inherited Create;
  fOwner := pOwner;
end;

end.

UPD1

我使用我的原则更新了您的示例。在这个过程中,我意识到你的主要问题是处理事实上顶级项目实际上是一个列表。您正在尝试使用复杂的构造向编译器解释这一点,但这不是实现它的方法。

unit Unitgenerics;

interface

uses
  System.Generics.Collections;

type
  TGItem<TOwner : class> = class
  private
    fOwner: TOwner;
  public
    constructor Create( const pOwner : TOwner );
    property Owner : TOwner
             read fOwner;
  end;

  TRealItem< TOwner : class > = class;

  TRealOwner<TOwner : class> = class( TObjectList<TRealItem< TOwner >> )
    // Items are already of type TRealItem
    // add some properties here
  end;

  TRealItem< TOwner : class > = class(TGItem< TRealOwner<TOwner> > )
    // Owner already defined and is of type TRealOwner
    // add some properties here
  end;

  T2ndLevelItem = class;
  T2ndLevelOwner = class;

  T2ndLevelOwner = class( TRealOwner< T2ndLevelOwner > )

  end;

  T2ndLevelItem = class( TRealItem< T2ndLevelOwner > )

  end;

  TInheritable2ndLevelItem< TOwner : class> = class;
  TInheritable2ndLevelOwner< TOwner : class> = class;

  TInheritable2ndLevelOwner< TOwner : class> = class( TRealOwner< TOwner > )

  end;

  TInheritable2ndLevelItem< TOwner : class> = class( TRealItem< TOwner > )

  end;

  T3rdLevelItem = class;
  T3rdLevelOwner = class;

  T3rdLevelOwner = class( TRealOwner< T3rdLevelOwner > )

  end;

  T3rdLevelItem = class( TRealItem< T3rdLevelOwner > )

  end;

  TInheritable3rdLevelItem< TOwner : class> = class;
  TInheritable3rdLevelOwner< TOwner : class> = class;

  TInheritable3rdLevelOwner< TOwner : class> = class( TInheritable2ndLevelOwner< TOwner > )

  end;

  TInheritable3rdLevelItem< TOwner : class> = class( TInheritable2ndLevelItem< TOwner > )

  end;



implementation

{ TGItem<TOwner> }

constructor TGItem<TOwner>.Create(const pOwner: TOwner);
begin
  inherited Create;
  fOwner := pOwner;
end;

end.

我希望这会有所帮助。