Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
ОСНОВЫ ОФИСНОГО ПРОГРАММИРОВАНИЯ И ЯЗЫК VBA - 2....doc
Скачиваний:
79
Добавлен:
17.12.2018
Размер:
1.62 Mб
Скачать

Обертывание коллекции vba

Обратите внимание, наш только что приведенный пример ВводСписка, иллюстрирующий часто возникающую на практике задачу, невозможно реализовать, используя встроенный класс Collection.Причина этого уже пояснялась, - коллекция хранит ссылки на созданные вне нее элементы, она не обладает собственной памятью. Поэтому пользователь сам должен заботиться о создании и хранении элементов. Конечно, хотелось бы поручить эту задачу самой коллекции, как это делается в созданном динамическом классе СписокЛичностей. С другой стороны этот класс имеет значительно менее мощные методы AddFirst и AddLast в сравнении с методом Add класса Collection. Класс СписокЛичностей не позволяет создавать словари, он значительно менее эффективен, чем класс Collection в тех случаях, когда требуется обеспечить прямой доступ к элементам списка. Возникает вопрос, нельзя ли соединить достоинства двух подходов? Оказывается, можно. Для этого применяется стандартная при работе с классами технология обертывания. Суть ее в том, что стандартный объект, например, элемент управления встраивается в собственный класс. Методы класса могут вызывать методы объекта, сохраняя тем самым мощь стандартного объекта. С другой стороны можно добавить новые методы или расширить функциональность существующих. Мы продемонстрируем эту технологию, показав как можно строить собственные коллекции для объектов любого класса. Конечно, нам не удастся построить универсальный класс, расширяющий возможности класса Collection, но нам удастся показать, что для каждого собственного класса элементов, можно построить коллекцию, обладающую всеми свойствами обычной коллекции, но, вместе с тем, хранящую элементы в самой коллекции.

Не будем изобретать новых примеров и построим класс КоллекцияЛичностей. Этот класс будет обладать всеми стандартными свойствами и методами класса Collection, дополненными методами класса СписокЛичностей. Он будет иметь:

  1. Свойство Count - возвращает число элементов коллекции. Доступно только для чтения, имеет тип возвращаемого значения Long.

  2. Метод AddPerson (item, key, before, after) - добавляет элементы в коллекцию. Первый параметр Item является обязательным и задает добавляемый элемент. Параметр key - необязателен, он задается, когда элементу ставится в соответствие ключ. Два последних необязательных параметра уточняют позицию вставки, - задают индекс или ключ элемента, перед или после которого добавляется новый элемент. Только один из этих параметров может быть задан. Если не задан ни один, элемент добавляется в конец коллекции. Заметьте, несмотря на внешнее сходство метод AddPerson отличается от метода Add класса Collection. Во-первых, параметр Item будет иметь строго фиксированный тип Личность. Во-вторых, есть различия в реализации. Коллекция будет хранить не ссылку на элемент Item класса Личность, а копию этого элемента.

  3. Метод Remove(key) - удаляет элементы коллекции. Удаляется элемент с заданным ключом: заметьте, это может быть индекс элемента. После удаления происходит перенумерация элементов и уменьшается счетчик Count.

  4. Метод Item(key) - возвращает значение элемента списка с заданным ключом. И здесь в роли ключа может выступать обычный индекс элемента.

  5. Метод PrintList() - печатает элементы списка в его текущем состоянии.

  6. Метод PrintHystory - печатает историю создания списка, - все его элементы, даже если они и были удалены впоследствии.

Наряду с открытыми свойствами и методами наш класс будет содержать и закрытые свойства и методы:

  1. Свойство First - указатель на начало списка, хранящего элементы коллекции.

  2. Событие Initialize - конструктор по умолчанию, инициализирует список.

  3. Метод AddFirst - создает копии элементов и добавляет их в список, представляющий внутреннюю память коллекции.

Вот полное описание класса КоллекцияЛичностей, обертывающего класс Collection. Класс сохранил все мощные свойства стандартной коллекции и, к тому же, приобрел новые привлекательные свойства:

Option Explicit

'Определение класса КоллекцияЛичностей

'Свойства

Private First As ЭлементСпискаЛичностей

Private Count As Long

'Обратите внимание: встраиваем коллекцию

Private Persons As Collection

'Методы

Private Sub Class_Initialize()

Set First = Nothing

Count = 0

'при создании объекта класса КоллекцияЛичностей создается

'и внутренняя коллекция для хранения его элементов

Set Persons = New Collection

End Sub

Private Sub AddFirst(F As Личность)

Dim Elem As New ЭлементСпискаЛичностей

Dim Info As New Личность

'Создаем копию переменной F. В списке будем использовать копию, а не ссылку.

Info.CopyPerson F

Set Elem.Сам = Info

Set Elem.Друг = First

Set First = Elem

Count = Count + 1

End Sub

Public Sub PrintList()

'Печать текущего состояния списка

Dim i As Long

For i = 1 To Persons.Count

Persons(i).PrintPerson

Next i

End Sub

Public Sub AddPerson(Item As Личность, Optional key As String = "", _

Optional before As Long = 0, Optional after As Long = 0)

Dim P As Личность

'Вначале добавляем элемент в линейный список,

'используя внутренний метод AddFirst

AddFirst Item

'Разбор случаев вызова

Set P = First.Сам

If key <> "" Then

Persons.Add P, key

ElseIf before <> 0 Then

Persons.Add P,, before

ElseIf after <> 0 Then

Persons.Add P,,, after

Else

Persons.Add P

End If

Count = Persons.Count

End Sub

Public Property Get Количество() As Long

Количество = Count

End Property

Public Sub Remove(key As Variant)

Persons.Remove key

Count = Persons.Count

End Sub

Public Function Item(key As Variant) As Личность

Set Item = Persons.Item(key)

End Function

Public Sub PrintHystory()

'Печать всех элементов в порядке, обратном их добавлению в список.

'Печатаются все элементы, независимо от того, были ли они удалены.

Dim P As ЭлементСпискаЛичностей

Dim Q As Личность

Set P = First

While Not (P Is Nothing)

Set Q = P.Сам

Q.PrintPerson

Set P = P.Друг

Wend

End Sub

Пример 5.11. (html, txt)

Приведем комментарии к этому тексту:

  1. Прежде всего, следует обратить внимание на то, что объект Persons класса Collection встроен, как свойство, в создаваемый класс. Этот объект инициализируется конструктором по умолчанию, - обработчиком события Initialize в тот момент, когда создается сам объект класса КоллекцияЛичностей. Благодаря этому встроенному объекту, становятся доступны все свойства и методы стандартного класса Collection.

  2. Наиболее интересна реализация метода AddPerson. В этом методе вначале вызывается закрытый метод AddFirst, создающий копию добавляемого элемента и помещающий эту копию в линейный список, представляющий внутреннюю память коллекции. Следующим шагом является вызов метода Add встроенным объектом Persons. Этому предшествует разбор случаев вызова. Дело в том, что метод AddPerson, также как и его прародитель метод Add имеет возможные параметры, которые могут быть заданы или опущены в момент вызова. Так что приходится разбираться, какие из параметров были заданы при вызове.

  3. Функция Item и метод Remove достаточно просты в реализации, они вызывают соответствующие методы встроенного объекта.

  4. Свойство Count, доступное для чтения, соответствует значению этого свойства внутреннего объекта Persons.

  5. В интерфейс класса добавлены два метода печати элементов списка. Один из них распечатывает текущее состояние коллекции, состав элементов которой может динамически изменяться. Другой метод распечатывает историю создания коллекции. Список, представляющий внутреннюю память коллекции, сохраняет эту историю

Приведем процедуру для работы с этой коллекцией:

Public Sub Великие()

'Создание и работа с коллекцией личностей

Dim Личности As New КоллекцияЛичностей

Dim ЭтоЛичность As Личность

'Работа с коллекцией, как со списком

Dim Адам As New Личность

Адам.InitPerson "Адам", "Первый Человек", #1/1/100#

Личности.AddPerson Адам, "Первый"

Dim Ной As New Личность

Ной.InitPerson "Ной", "Праведник", #1/1/100#

Личности.AddPerson Item:=Ной, after:=1

'Работа с коллекцией, как с динамическим массивом

Dim Шекспир As New Личность

Шекспир.InitPerson "Вильям", "Шекспир", #4/23/1564#

Личности.AddPerson Item:=Шекспир, after:=2

Dim Гомер As New Личность

Гомер.InitPerson "Гомер", "Великий Слепой", #1/1/100#

Личности.AddPerson Item:=Гомер, before:=3

'Работа с коллекцией, как со словарем

Dim Булгаков As New Личность

Булгаков.InitPerson "Михаил", "Булгаков", #1/23/1891#

Личности.AddPerson Item:=Булгаков, key:="Мастер"

Dim Пушкин As New Личность

Пушкин.InitPerson "Александр", "Пушкин", #6/6/1799#

Личности.AddPerson Item:=Пушкин, key:="Гений"

'Печать всего списка

Личности.PrintList

Debug.Print Личности.Количество

'Удаление элементов

Личности.Remove "Первый"

Личности.Remove 2

'Печать после удаления

Личности.PrintList

Debug.Print Личности.Количество

'Доступ к отдельным элементам по ключу

Set ЭтоЛичность = Личности.Item("Гений")

ЭтоЛичность.PrintPerson

Set ЭтоЛичность = Личности.Item(2)

ЭтоЛичность.PrintPerson

End Sub

Пример 5.12. (html, txt)

Вот результаты отладочной печати при работе этой процедуры:

Адам Первый Человек родился 01.01.100

Ной Праведник родился 01.01.100

Гомер Великий Слепой родился 01.01.100

Вильям Шекспир родился 23.04.1564

Михаил Булгаков родился 23.01.1891

Александр Пушкин родился 06.06.1799

6

Ной Праведник родился 01.01.100

Вильям Шекспир родился 23.04.1564

Михаил Булгаков родился 23.01.1891

Александр Пушкин родился 06.06.1799

4

Александр Пушкин родился 06.06.1799

Вильям Шекспир родился 23.04.1564

Приведем теперь текст процедуры, позволяющей создавать коллекцию личностей в процессе диалога с пользователем. Ранее мы отмечали, что такая процедура не может корректно работать с обычной коллекцией. С нашим же построенным классом никаких проблем нет:

Public Sub ВводКоллекции()

'Создание коллекции в диалоге с пользователем

Dim Личности As New КоллекцияЛичностей

Dim ЭтоЛичность As New Личность

Dim Имя As String, Фамилия As String, Дата As Date

If MsgBox("Начнем создавать список личностей?", vbYesNo) = vbYes Then

Do

ЭтоЛичность.ВашеИмя = InputBox("Введите имя личности")

ЭтоЛичность.ВашаФамилия = InputBox("Введите Фамилию")

ЭтоЛичность.ВашаДатаРождения = InputBox("Введите дату рождения ")

Личности.AddPerson ЭтоЛичность

Loop Until MsgBox("Продолжим создавать список личностей?", vbYesNo) = vbNo

Личности.PrintList

End If

End Sub

На этом мы и поставим точку в этой лекции.

Программный код большинства примеров данной лекции можно найти в проектах, доступных для просмотра: DocOne6, DocTwo6.