2010-11-04 1 views
0

Я знал, как это сделать, но забыл снова ... Довольно раздражает, потому что я работаю над классом, который содержит список файлов XML, и теперь я просто хочу использовать цикл for-in для просмотра всех файлов в этом списке. Это класс, я прямо сейчас:
Перечисление пользовательского массива, чтобы я мог использовать for-in

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; 

Выглядит смешно? :-) Я знаю, но я хочу скрыть определение класса TXmlFile для внешнего мира. В принципе, класс TXmlFileList позволяет мне просто ссылаться на XmlFileList [I], чтобы получить файл в позиции I. Хорошо работает.

Но теперь я хочу прокрутить элементы TXmlFileList.TItem, поэтому мне нужно показать класс TXmlFileList.TItem. Однако этого недостаточно. Ему тоже нужен перечислитель в классе TXmlFileList!
Как создать этот счетчик?



Возможно, вам интересно, почему я использую эту сложную конструкцию. Ну, это может быть сложно, но он будет использоваться некоторыми другими разработчиками, и я не хочу предлагать больше методов, чем нужно. Таким образом, я даю им методы «Добавить», «Очистить» и «Подсчитать», чтобы прокрутить список, и любое свойство, определенное в самом TItem. Им не нужно больше этого, хотя я мог бы добавить еще несколько функций позже ...

ответ

2

Найденный! Мне нужно создать новый класс, который я назвал TItemEnumerator, и я также включил его в классе TXmlFileList, сразу после 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; 

Внедрение было легко, просто дополнительный метод TXmlFileList:

function GetEnumerator: TItemEnumerator; 

И, наконец, обнажив класс с внешним миром, который я сделал, добавив в TXmlFileList:

type TXmlFile = TXmlFileList.TItem; 

Да, это грязно! :-)


Это приводит к этому коду:

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; 

И да, вы можете начать чесать голову, когда смотришь на него, но это отличное решение, чтобы скрыть множество функций от неопытных разработчиков ! Теперь они видят только то, что им нужно видеть. (И, надеюсь, они никогда не смотреть на этот исходный код!)
я просто ожидал, что мне нужно писать больше кода, чем это ...


Почему обнажая тип TItem с другим именем? На самом деле, эти классы снова завертываются в более крупный класс TXmlManager, который также обрабатывает таблицы стилей, преобразования, проверки и множество других вещей. TXmlFile фактически отображается на уровне TXmlManager, а не в классе TXmlFileList. TXmlManager содержит несколько других подобных списков, где я просто повторно использую имя TItem. Однако конфликтов нет, потому что родители должны быть добавлены для обозначения соответствующего типа класса.
И хотя заголовок класса может выглядеть сложным с почти 200 строками кода, остальная часть устройства довольно тонкая, с почти 700 строками кода и множеством комментариев. Структура класса фактически помогает мне сделать все это просто с точки зрения тех, кто его использует. Те, кто его использует, не должны искать типы и методы использования. Их выбор очень ограничен ...

+1

Зачем делать 'TXmlFileList.TItem' частным, если позже вы все равно его выставите? –

+0

Хороший вопрос.:-) В принципе, я просто следовал за шаблоном, и выставляя класс, это означало разрыв шаблона. Кроме того, я всегда могу решить, чтобы перечислитель возвращал только любой другой тип, если мне нравится. Например, только подписи. Но тогда я должен был бы объявить TXmlFile немного выше, чем публичный. –

+1

В Примозе есть серия статей по счетчикам, которые могут оказаться полезными: http://www.thedelphigeek.com/search/label/enumerators – 2010-11-05 01:49:30