2010-11-04 41 views
0

Je savais comment faire cela, mais j'ai encore oublié ... Assez irritant, parce que je travaille sur une classe qui contient une liste de fichiers XML, et maintenant je veux juste utiliser une boucle for-in pour parcourir tous les fichiers de cette liste. Ceci est la classe que j'ai en ce moment:
Énumération d'un tableau personnalisé afin que je puisse utiliser

type 
    TXmlFileList = class(TInterfacedObject) 
    private 
    type 
     TItem = class(TInterfacedObject) 
     strict private 
     FCaption: string; 
     protected 
     constructor Create(const ACaption: string; const AXML: WideString); 
     public 
     destructor Destroy; override; 
     property Caption: string read FCaption; 
     end; 
    strict private 
    FXmlFiles: array of TXmlFileList.TItem; 
    strict protected 
    function GetXmlFile(index: Integer): TXmlFileList.TItem; 
    public 
    constructor Create(); 
    destructor Destroy; override; 
    function Add(const ACaption: string; const AXML: WideString): Integer; overload; 
    function Add(const AFilename: string): Integer; overload; 
    function Count: Integer; 
    procedure Clear; 
    property XmlFile[ index: Integer ]: TXmlFileList.TItem read GetXmlFile; default; 
    end; 

un drôle d'air? :-) Je sais, mais je veux cacher la définition de la classe TXmlFile au monde extérieur. Fondamentalement, la classe TXmlFileList me permet de simplement faire référence à XmlFileList [I] pour obtenir le fichier en position I. Fonctionne bien. Mais maintenant je veux parcourir les éléments TXmlFileList.TItem, donc je dois exposer la classe TXmlFileList.TItem. Ce n'est pas assez, cependant. Il a aussi besoin d'un énumérateur dans la classe TXmlFileList!
Comment créer cet énumérateur?



Vous vous demandez probablement pourquoi j'utilise cette construction complexe. Eh bien, cela peut être complexe mais il sera utilisé par d'autres développeurs et je ne veux pas fournir plus de méthodes que nécessaire. De cette façon, je leur donne seulement les méthodes "Add", "Clear" et "Count" pour faire une boucle dans la liste, et toute propriété définie sur le TItem lui-même. Ils n'ont pas besoin de plus que cela, bien que je pourrais ajouter quelques fonctionnalités plus tard ...

Répondre

2

Trouvé! Je avais besoin de créer une nouvelle classe, que je l'ai appelé TItemEnumerator, et je l'ai aussi inclus dans la classe TXmlFileList, juste après TItem:

type 
     TItemEnumerator = class(TObject) 
     strict private 
     FOwner: TXmlFileList; 
     FIndex: Integer; 
     protected 
     constructor Create(AOwner: TXmlFileList); 
     public 
     function GetCurrent: TItem; 
     function MoveNext: Boolean; 
     property Current: TItem read GetCurrent; 
     end; 

La mise en œuvre est facile, juste une méthode supplémentaire pour TXmlFileList:

function GetEnumerator: TItemEnumerator; 

Et enfin, ce qui expose la classe au monde extérieur, que je l'ai fait en ajoutant à ce TXmlFileList:

type TXmlFile = TXmlFileList.TItem; 

Ouais, ce sale! :-)


Il en résulte ce code:

type 
    TXmlFileList = class(TInterfacedObject) 
    private 
    type 
     TItem = class(TInterfacedObject) 
     strict private 
     FCaption: string; 
     protected 
     constructor Create(const ACaption: string; const AXML: WideString); 
     public 
     destructor Destroy; override; 
     property Caption: string read FCaption; 
     end; 
    type 
     TItemEnumerator = class(TObject) 
     strict private 
     FOwner: TXmlFileList; 
     FIndex: Integer; 
     protected 
     constructor Create(AOwner: TXmlFileList); 
     public 
     function GetCurrent: TItem; 
     function MoveNext: Boolean; 
     property Current: TItem read GetCurrent; 
     end; 
    strict private 
    FXmlFiles: array of TXmlFileList.TItem; 
    strict protected 
    function GetXmlFile(index: Integer): TXmlFileList.TItem; 
    public 
    type TXmlFile = TXmlFileList.TItem; 
    constructor Create(); 
    destructor Destroy; override; 
    function Add(const ACaption: string; const AXML: WideString): Integer; overload; 
    function Add(const AFilename: string): Integer; overload; 
    function Count: Integer; 
    function GetEnumerator: TItemEnumerator; 
    procedure Clear; 
    property XmlFile[ index: Integer ]: TXmlFileList.TItem read GetXmlFile; default; 
    end; 

Et oui, vous pouvez commencer à vous gratter la tête quand on regarde, mais il est une excellente solution pour cacher de nombreuses fonctions de développeurs inexpérimentés ! Maintenant, ils ne voient que ce qu'ils ont besoin de voir. (Et nous espérons qu'ils ne regardent jamais ce sourcecode!)
Je me attendais que je devais écrire un code plus que cela ...


Pourquoi exposer le type de TItem avec un nom différent? En fait, ces classes sont enveloppées à nouveau dans une classe plus grande, TXmlManager, qui gère également les feuilles de style, les transformations, les validations et bien d'autres choses. TXmlFile est réellement exposé au niveau TXmlManager, pas à la classe TXmlFileList. TXmlManager contient quelques autres listes similaires où je viens de réutiliser le nom TItem. Il n'y a pas de conflits, cependant, parce que les parents doivent être ajoutés pour se référer au type de classe approprié.
Et tandis que l'en-tête de classe peut sembler complexe avec près de 200 lignes de code, le reste de l'unité est assez mince, avec près de 700 lignes de code et beaucoup de commentaires. La structure de la classe m'aide à faire en sorte que tout cela se fasse simplement du point de vue de ceux qui l'utilisent. Ceux qui l'utilisent n'ont pas besoin de chercher les types et les méthodes à utiliser. Leurs choix sont juste très limités ...

+1

Pourquoi rendre 'TXmlFileList.TItem' privé si plus tard vous l'exposez quand même? –

+0

Bonne question.:-) Fondamentalement, je viens de suivre un modèle et exposer la classe signifiait briser le modèle. D'ailleurs, je peux toujours décider d'avoir l'énumérateur pour renvoyer n'importe quel autre type, si je veux. Par exemple, juste les légendes. Mais alors je devrais déclarer TXmlFile un peu plus haut comme public. –

+1

Primoz a une série d'articles sur les enquêteurs que vous pourriez trouver utile: http://www.thedelphigeek.com/search/label/enumerators –