質問

さて、これは混乱するかもしれません。私がやろうとしているのは、列挙者を使用して、クラスタイプに基づいて汎用リストの特定のアイテムのみを返すことです。

次の階層を与えられます:

type
    TShapeClass = class of TShape;

    TShape = class(TObject)
    private
        FId: Integer;
    public
        function ToString: string; override;
        property Id: Integer read FId write FId;
    end;

    TCircle = class(TShape)
    private
        FDiameter: Integer;
    public
        property Diameter: Integer read FDiameter write FDiameter;
    end;

    TSquare = class(TShape)
    private
        FSideLength: Integer;
    public
        property SideLength: Integer read FSideLength write FSideLength;
    end;

    TShapeList = class(TObjectList<TShape>)
    end;

どうすれば拡張できますか TShapeList そのように、私は次のようなことをすることができます:

procedure Foo;
var
    ShapeList: TShapeList;
    Shape: TShape;
    Circle: TCircle;
    Square: TSquare;

begin
    // Create ShapeList and fill with TCircles and TSquares
    for Circle in ShapeList<TCircle> do begin
        // do something with each TCircle in ShapeList
    end;
    for Square in ShapeList<TSquare> do begin
        // do something with each TSquare in ShapeList
    end;
    for Shape in ShapeList<TShape> do begin
        // do something with every object in TShapeList
    end;
end;

拡張しようとしました TShapeList PrimozGabrijelcicのビットの適応バージョンを使用して パラメーター化された列挙器 次のように工場記録を使用してください:

type
    TShapeList = class(TObjectList<TShape>)
    public
        type
            TShapeFilterEnumerator<T: TShape> = record
            private
                FShapeList: TShapeList;
                FClass: TShapeClass;
                FIndex: Integer;
                function GetCurrent: T;
            public
                constructor Create(ShapeList: TShapeList);
                function MoveNext: Boolean;
                property Current: T read GetCurrent;
            end;

            TShapeFilterFactory<T: TShape> = record
            private
                FShapeList: TShapeList;
            public
                constructor Create(ShapeList: TShapeList);
                function GetEnumerator: TShapeFilterEnumerator<T>;
            end;

        function FilteredEnumerator<T: TShape>: TShapeFilterFactory<T>;
    end;

それから私は修正しました Foo することが:

procedure Foo;
var
    ShapeList: TShapeList;
    Shape: TShape;
    Circle: TCircle;
    Square: TSquare;

begin
    // Create ShapeList and fill with TCircles and TSquares
    for Circle in ShapeList.FilteredEnumerator<TCircle> do begin
        // do something with each TCircle in ShapeList
    end;
    for Square in ShapeList.FilteredEnumerator<TSquare> do begin
        // do something with each TSquare in ShapeList
    end;
    for Shape in ShapeList.FilteredEnumerator<TShape> do begin
        // do something with every object in TShapeList
    end;
end;

しかし、Delphi 2010はコンパイルしようとするとエラーを投げかけています FooIncompatible types: TCircle and TShape. 。コメントした場合 TCircle ループ、その後、同様のエラーが発生します TSquare. 。コメントしたら TSquare ループアウトも同様に、コードをコンパイルして動作させます。まあ、それはすべてのオブジェクトがすべてから降りるので、それはすべてのオブジェクトを列挙するという意味で機能します TShape. 。奇妙なことに、コンパイラが示す行番号は、ファイルの端を超えて2行です。私のデモプロジェクトでは、177行を示しましたが、175行しかありません。

これを機能させる方法はありますか?タイプキャストやチェックインを行わずに直接サークルに割り当てることができます for ループ自体。

役に立ちましたか?

解決

ここでは見せませんが、問題はおそらくの実装にあります GetCurrent.

コンパイラが次のようなものを受け入れている間

result := FShapeList[FIndex];

ジェネリッククラスでは、結果クラスが等しくないときにこれは失敗します tshape, 、そうです tcircletsquare. 。そのため、3番目のループで機能します。

コードを変更します

result := T(FShapeList[FIndex]);

そして、あなたは大丈夫です。

エラーの存在しない行数は、コンパイラによって行われた内部汎用を解決したためです。

他のヒント

列挙器で直接それを行うことはできません。私はあなたが「IS」にこだわっているのではないかと心配しています。唯一の方法は、実際にヘルパー列挙器を作成することです。あなたの場合、コンパイラを不幸にしている唯一のものが見つかるまで、行にコメントしてみてください。

申し訳ありませんが、それは私が提案できるすべてです。

あなたはあなたの質問に答えを書きました:)

正しい関数を呼び出す必要があります。

for Circle in ShapeList.FilteredEnumerator<TCircle> do
  //blah blah

for Square in ShapeList.FilteredEnumerator<TSquare> do
  //blah blah

あなたのコードについての1つの小さな発言:あなたはあなたを捨てることができます TShapeFilterFactory 記録し、単にメソッドを追加します。

TShapeFilterEnumerator<T>.GetEnumerator : TShapeFilterEnumerator<T>;
begin
  Result := Self;
end;
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top