1

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

次の階層があるとします。

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;

Fooただし、についてコンパイルしようとすると、Delphi2010がエラーをスローしますIncompatible types: TCircle and TShape。ループをコメントアウトすると、TCircleについて同様のエラーが発生しTSquareます。ループについてもコメントするTSquareと、コードがコンパイルされて機能します。それは、すべてのオブジェクトがから派生しているため、すべてのオブジェクトを列挙するという意味で機能しますTShape。奇妙なことに、コンパイラが示す行番号は、ファイルの終わりを2行超えています。私のデモプロジェクトでは、177行目が示されていましたが、175行しかありません。

これを機能させる方法はありますか?forタイプキャストを実行したり、ループ自体をチェックインしたりせずに、Circleに直接割り当てることができるようにしたいと思います。

4

3 に答える 3

3

ここでは示していませんが、問題はおそらくGetCurrentの実装にあります。

コンパイラは次のようなものを受け入れますが

result := FShapeList[FIndex];

ジェネリッククラスでは、結果クラスがTShapeと等しくない場合、これは失敗します。これは、 TCircleTSquareの場合です。これが、3番目のループで機能する理由です。

コードをに変更します

result := T(FShapeList[FIndex]);

そしてあなたは元気です。

エラーの行番号が存在しないのは、行番号に適切にマップされていないコンパイラによって実行された内部ジェネリックを解決したためです。

于 2010-04-30T15:25:43.120 に答える
0

列挙子で直接それを行うことはできません。あなたは「is」で立ち往生しているのではないかと思います。唯一の方法は、実際にヘルパー列挙子を作成することです。あなたの場合、コンパイラを不幸にしている唯一のものが見つかるまで、行をコメントアウトしてみてください。

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

于 2010-04-30T06:58:25.127 に答える
0

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

適切な関数を呼び出す必要があります:

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;
于 2010-04-30T07:18:50.043 に答える