5

TObjectList<T>アプリのオブジェクト リスト間で共通の機能を処理する子孫を作成したいと考えています。次に、必要に応じて追加機能を導入するために、その新しいクラスからさらに派生したいと考えています。複数レベルの継承を使用して機能させることができないようです。おそらくジェネリックをもう少し理解する必要がありますが、成功せずにこれを行う正しい方法を高低で検索しました。これまでの私のコードは次のとおりです。

unit edGenerics;

interface

uses
  Generics.Collections;

type

  TObjectBase = class
  public
    procedure SomeBaseFunction;
  end;

  TObjectBaseList<T: TObjectBase> = class(TObjectList<T>)
  public
    procedure SomeOtherBaseFunction;
  end;

  TIndexedObject = class(TObjectBase)
  protected
    FIndex: Integer;
  public
    property Index: Integer read FIndex write FIndex;
  end;

  TIndexedObjectList<T: TIndexedObject> = class(TObjectBaseList<T>)
  private
    function GetNextAutoIndex: Integer;
  public
    function Add(AObject: T): Integer;
    function ItemByIndex(AIndex: Integer): T;
    procedure Insert(AIndex: Integer; AObject: T);
  end;

  TCatalogueItem = class(TIndexedObject)
  private
    FID: integer;
  public
    property ID: integer read FId write FId;
  end;

  TCatalogueItemList = class(TIndexedObjectList<TCatalogueItem>)
  public
    function GetRowById(AId: Integer): Integer;
  end;

implementation

uses
  Math;

{ TObjectBase }

procedure TObjectBase.SomeBaseFunction;
begin
end;

{ TObjectBaseList<T> }

procedure TObjectBaseList<T>.SomeOtherBaseFunction;
begin
end;

{ TIndexedObjectList }

function TIndexedObjectList<T>.Add(AObject: T): Integer;
begin
  AObject.Index := GetNextAutoIndex;
  Result := inherited Add(AObject);
end;

procedure TIndexedObjectList<T>.Insert(AIndex: Integer; AObject: T);
begin
  AObject.Index := GetNextAutoIndex;
  inherited Insert(AIndex, AObject);
end;

function TIndexedObjectList<T>.ItemByIndex(AIndex: Integer): T;
var
  I: Integer;
begin
  Result := Default(T);
  while (Count > 0) and (I < Count) and (Result = Default(T)) do
    if Items[I].Index = AIndex then
      Result := Items[I]
    else
      Inc(I);
end;

function TIndexedObjectList<T>.GetNextAutoIndex: Integer;
var
  I: Integer;
begin
  Result := 0;
  for I := 0 to Count - 1 do
    Result := Max(Result, Items[I].Index);
  Inc(Result);
end;

{ TCatalogueItemList }

function TCatalogueItemList.GetRowById(AId: Integer): Integer;
var
  I: Integer;
begin
  Result := -1;
  for I := 0 to Pred(Self.Count) do
    if Self.Items[I].Id = AId then
    begin
      Result := I;
      Break;
    end;
end;

end.
/////// ERROR HAPPENS HERE ////// ???? why is beyond me

次の宣言のようです。

>>> TCatalogueItemList = class(TIndexedObjectList<TCatalogueItem>) <<<<

次のコンパイラ エラーが発生します。

[DCC エラー] edGenerics.pas(106): E2010 互換性のない型: 'TCatalogueItem' および 'TIndexedObject'

ただし、コンパイラは、宣言自体ではなく、コンパイルされたユニットの END (106 行目) にエラーを表示します。これは私には意味がありません...

基本的には、必要に応じて新しい機能で拡張できる TObjectList から派生したジェネリック リストがあるという考えです。これに関するヘルプは素晴らしいでしょう!!!

Delphi 2010 を使用して追加する必要があります。

ありがとう。

4

1 に答える 1

9

エラーは型キャストにあり、コンパイラエラーは問題ありません(ただし、Delphi XE3で正しいファイルを見つけることができません)。

ItemByIndexメソッドが宣言されています:

TIndexedObjectList<T>.ItemByIndex(AIndex: Integer): T;

しかし、あなたは次の行を持っています:

Result := TIndexedObject(nil);

これは、関数の結果が型であるクラスでは問題ありませんが、関数の結果が型である子孫クラスでは問題ありません。TIndexedObjectListTIndexedObject TCatalogueItemListTCatalogueItem

ご存知かもしれませんが、TCatalogueItemインスタンスは変数と互換性のある割り当てTIndexedObjectですが、その逆は当てはまりません。これは次のように変換されます。

function TCatalogueItemList.ItemByIndex(AIndex: Integer): TCatalogueItem;
begin
  Result := TIndexedObject(nil);  //did you see the problem now?

結果をnil値に初期化するには、次のようにDefault()疑似関数を呼び出すことができます。

Result := Default(T);

Delphi XE以降では、ソリューションも一般的です。結果を固定TIndexedObjectListクラスとして型キャストするのではなく、T型を使用してジェネリック型キャストを適用します

Result := T(nil);
//or 
Result := T(SomeOtherValue);

ただし、この特定のケースでは、定数を型キャストするnil必要はありません。これnilは、任意の参照と割り当て互換性のある特別な値であるため、行を次のように置き換える必要があります。

Result := nil;

そしてそれはコンパイルされ、うまくいけばあなたが期待するように動作します。

于 2013-02-21T02:38:48.610 に答える