Delphi:列挙器を使用してtlist をクラスタイプでフィルタリングしますか?
-
02-10-2019 - |
質問
さて、これは混乱するかもしれません。私がやろうとしているのは、列挙者を使用して、クラスタイプに基づいて汎用リストの特定のアイテムのみを返すことです。
次の階層を与えられます:
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はコンパイルしようとするとエラーを投げかけています Foo
約 Incompatible types: TCircle and TShape
. 。コメントした場合 TCircle
ループ、その後、同様のエラーが発生します TSquare
. 。コメントしたら TSquare
ループアウトも同様に、コードをコンパイルして動作させます。まあ、それはすべてのオブジェクトがすべてから降りるので、それはすべてのオブジェクトを列挙するという意味で機能します TShape
. 。奇妙なことに、コンパイラが示す行番号は、ファイルの端を超えて2行です。私のデモプロジェクトでは、177行を示しましたが、175行しかありません。
これを機能させる方法はありますか?タイプキャストやチェックインを行わずに直接サークルに割り当てることができます for
ループ自体。
解決
ここでは見せませんが、問題はおそらくの実装にあります GetCurrent.
コンパイラが次のようなものを受け入れている間
result := FShapeList[FIndex];
ジェネリッククラスでは、結果クラスが等しくないときにこれは失敗します tshape, 、そうです tcircle と tsquare. 。そのため、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;