Delphi: Utilisation énumérateurs pour filtre TList par type de classe?
-
02-10-2019 - |
Question
D'accord, cela pourrait être source de confusion. Ce que je suis en train de faire est d'utiliser un recenseur à ne renvoyer certains éléments dans une liste générique en fonction du type de classe.
Compte tenu de la hiérarchie suivante:
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;
Comment puis-je prolonger TShapeList
ce que je peux faire quelque chose de semblable à ce qui suit:
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;
J'ai essayé extension TShapeList
en utilisant une version adaptée du bit de Primoz Gabrijelcic sur paramétrés énumérateurs l'aide d'un enregistrement d'usine comme suit:
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;
Je modifié Foo
être:
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;
Cependant, Delphi 2010 jette une erreur lorsque je tente de compiler Foo
au sujet Incompatible types: TCircle and TShape
. Si je commente la boucle TCircle
, je reçois une erreur similaire au sujet TSquare
. Si je commente la boucle TSquare
comme bien, le code compile et œuvres. Eh bien, cela fonctionne dans le sens où elle énumère tous les objets car ils descendent tous de TShape
. La chose étrange est que le numéro de la ligne que le compilateur indique est de 2 lignes au-delà de la fin de mon dossier. Dans mon projet de démonstration, il a indiqué la ligne 177, mais il y a seulement 175 lignes.
Y at-il moyen de faire ce travail? Je voudrais pouvoir assigner Circle directement sans passer par les typecasts ou le contrôle dans ma boucle de for
lui-même.
La solution
Vous ne le montre pas, mais le problème réside probablement dans la mise en œuvre de GetCurrent .
Alors que le compilateur accepte quelque chose comme
result := FShapeList[FIndex];
dans la classe générique, ce échouera lorsque la classe de résultat ne correspond pas à TShape , ce qui est le cas pour TCircle et TSquare . Voilà pourquoi il travaille dans la troisième boucle.
Modifier le code
result := T(FShapeList[FIndex]);
et vous êtes bien.
Le numéro de ligne non-existante pour l'erreur est due à la résolution du générique interne fait par le compilateur qui ne sont pas bien adaptées aux numéros de ligne.
Autres conseils
Vous ne pouvez pas le faire directement dans le recenseur. Je crains que vous êtes coincé avec « est ». La seule façon est en effet de créer une recenseur d'aide. Dans votre cas, essayez de commenter les lignes jusqu'à ce que vous trouvez la seule que le compilateur de faire des malheureux.
Désolé qui est tout à fait tout ce que je peux suggérer.
Vous avez écrit la réponse dans votre question:)
Il vous suffit d'appeler la fonction droite:
for Circle in ShapeList.FilteredEnumerator<TCircle> do
//blah blah
for Square in ShapeList.FilteredEnumerator<TSquare> do
//blah blah
Une petite remarque au sujet de votre code: vous pouvez vider votre dossier de TShapeFilterFactory
, et il suffit d'ajouter une méthode:
TShapeFilterEnumerator<T>.GetEnumerator : TShapeFilterEnumerator<T>;
begin
Result := Self;
end;