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.

Était-ce utile?

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;
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top