При объектно-ориентированном программировании (ООП) подход иной. Данные и подпрограммы связаны между собой и описываются в классе.
Класс – сложный тип данных, в котором описывается структура объекта. Объект – основной строительный материал для написания программ. Отличные представители объектов – форма, лист, книга, диаграмма. Объект, созданный на основе класса, можно называть экземпляром класса.
Класс имеет следующую структуру:
Поле – элемент класса для хранения данных,
Свойство – элемент класса для хранения данных с возможностью их обработки,
Метод – аналог процедуры или функции,
Событие – сигнал при изменении состояния объекта, например исполнения метода или изменения данных.
Из всех принципов ООП в vba реализуемы только два: Абстрагирование и инкапсуляция.
Инкапсуляция позволяет скрыть механизм работы класса, оставив открытыми для других процедур только необходимые для работы с экземпляром класса.
Абстракция позволяет создать объект, наиболее приближенный к некомпьютерному прототипу. Например, создать объект «Кошка» со свойствами «количество лап» и «цвет хвоста».
Создание класса
Для создания класса в vba редакторе выберите в меню Insert строку Class Module. Назовите созданный класс путем переименования созданного модуля. В файле с примером он называется ExampleClass. А модуль, демонстрирующий использование этого класса называется ExClassManagement. В свойствах класса, кроме имени так же есть параметр Instancing. Указывается, будет ли виден класс из другой книги при установке ссылки на данную книгу. При установке Private (по умолчанию) класс виден только в данной книге, при установке PublicNotCreatable, класс не будет доступен из другой книги, однако экземпляр класса доступен будет, если он создан в данной книге.
Создание экземпляра класса
Класс — это всего лишь описание объекта. Для использования возможностей класса, необходимо создать экземпляр класса (объект). Существует несколько способов:
Способ 1:
Private Sub TestClass() Dim cl As ExampleClass Set cl = New ExampleClass End Sub
Данный способ корректен абсолютно
Способ 2:
Dim cl As ExampleClass Private Sub TestClass() Set cl = New ExampleClass End Sub
Этот способ отличается от первого способа тем, что экземпляр класса объявляется вне процедуры и работать с ним можно во всех процедурах модуля. При замене Dim на Public экземпляр класса доступен во всем проекте, если объявляется вне объектного модуля.
Способ 3:
Dim cl WithEvents As ExampleClass Private Sub TestClass() Set cl = New ExampleClass End Sub
Экземпляр класса объявляется с событиями, и если в классе описаны события объекта, они будут доступны. Работает только при объявлении в объектном модуле (модуль класса, формы, листа, книги)
Способ 4:
Private Sub TestClass() Dim cl As New ExampleClass End Sub
Так называемый неявный метод создания экземпляра класса. В этом случае объект создается при первом обращении к переменной cl. Наверное, предпочтительнее сначала объявлять переменную (выделяется память), а затем явно создавать объект.
Уничтожение экземпляра класса
Естественно, после использования экземпляра класса, необходимо очистить память. Делается это одним способом:
Set cl = Nothing
Если не деинициализировать экземпляр класс, то ничего глобально страшного происходить в современных офисных пакетах не будет. Но делать это нужно.
Создание полей класса
Созданный класс ExampleClass не имеет полей, свойств, методов, событий, поэтому и созданный на его основе объект (экземпляр класса) cl, так же бесполезен.
Поля это переменные класса, объявленные в его модуле. Поля бывают закрытые и открытые. Доступ к закрытым полям возможен только внутри модуля класса. Открытое поле, по сути – свойство класса, и при создании экземпляра класса оно будет доступно.
Создание закрытого поля:
Dim sBody As String
или
Private sBody As String
Создание открытого поля:
Public Head As String
И теперь свойство Head доступно у экземпляра класса cl.
В него можно записать
cl.Head = "FHead"
и прочитать
Debug.Print cl.Head
Создать поле с пользовательским типом данных не удастся.
Поля используются для хранения данных в объекте. Данные будут доступны, пока объект существует. Однако нельзя указать значение по-умолчанию для поля и нельзя сделать поле только для чтения.
Создание свойства класса
Свойство это способ доступа к данным внутри объекта. Выглядят как поля, однако, это функции (назовем их методами). Называются они Property Get для чтения данных из объекта, и Property Let для записи данных в объект. Есть еще третье Property Set для установки ссылки на другой объект. Но это можно сделать и при помощи Property Let, поэтому Property Set вещь бесполезная.
Синтаксис Property Get
[Public | Private | Friend] [Static] Property Get имя [(аргументы)] [As тип]
[произвольный код]
[имя=выражение]
[Exit Property]
[произвольный код]
[имя=выражение]
[End Property]
Элемент | Описание |
Public | Не обязательно. Делает метод открытым во всех модулях проекта |
Private | Не обязательно. Метод будет доступен только в модуле класса, то есть не будет виден как свойство |
Friend | Не обязательно. Метод будет виден во всех модулях проекта, но не будет виден по ссылке на класс. то есть при конструкции Dim cl as new ExampleClass; Dim cll as object; Set cll=cl в объекте cll свойство с модификатором Friend видно не будет, а в объекте cl будет |
Static | Не обязательно. Значения объявленных локальных переменных внутри метода сохраняются между обращениями. Не распространяется на переменные, объявленные на уровне модуля класса |
имя | Обязательно. Имя метода будет являться и именем свойства. при этом оно должно совпадать для методов Property Let или Property Set |
аргументы | Не обязательно. По сути, это аргументы функции. Имена и типы аргументов должны совпадать с аргументами метода Property Let |
тип | Не обязательно. Тип данных, возвращаемый функцией может быть Byte, Boolean, Integer, Long, Currency, Single, Double, Date, String, Object, Variant, пользовательским типом. Должен совпадать с типом данных выражения |
произвольный код | Не обязательно. Любое количество строк кода, например математические операции с возвращаемым значением |
выражение | Значение, возвращаемое методом Property Get. Если не указать, метод вернет пустую строку для типа String, False для Boolean, 0 для Integer и так далее |
Замечания:
— Элемент Exit Property означает выход из метода, и по сути аналог Exit Function. В методе их может быть несколько.
— Внутри метода Property Get может быть сколько угодно процедур и
функций, но сам метод не может входить в состав других процедур и
функций, одноименных методов Property Get в модуле класса быть не может.
Синтаксис Property Let
[Public | Private | Friend] [Static] Property Let имя ([аргументы,] значение)
[произвольный код]
[Exit Property]
[произвольный код]
[End Property]
Элемент | Описание |
Public | Не обязательно. Делает метод открытым во всех модулях проекта |
Private | Не обязательно. Метод будет доступен только в модуле класса, то есть не будет виден как свойство |
Friend | Не обязательно. Метод будет виден во всех модулях проекта, но не будет виден по ссылке на класс. то есть при конструкции Dim cl as new ExampleClass; Dim cll as object; Set cll=cl в объекте cll свойство с модификатором Friend видно не будет, а в объекте cl будет |
Static | Не обязательно. Значения объявленных локальных переменных внутри метода сохраняются между обращениями. Не распространяется на переменные, объявленные на уровне модуля класса |
аргументы | Не обязательно. По сути, это аргументы функции. Имена и типы аргументов должны совпадать с аргументами метода Property Get |
значение | Не обязательно. Имя переменной метода, которой будет присваиваться значения свойства. Тип данных значения должен совпадать с типом метода Property Get |
произвольный код | Не обязательно. Любое количество строк кода, например проверка указываемого значения. И конечно необходимо здесь передать значение из локальной переменной (значение) метода во внешнюю переменную модуля класса. А значение внешней переменной передать в выражение метода Property Get |
Замечание:
— Аналогично методу Property Get
Синтаксис Property Set
[Public | Private | Friend] [Static] Property Set имя ([аргументы,] ссылка)
[произвольный код]
[Exit Property]
[произвольный код]
[End Property]
Элемент | Описание |
Public | Не обязательно. Делает метод открытым во всех модулях проекта |
Private | Не обязательно. Метод будет доступен только в модуле класса, то есть не будет виден как свойство |
Friend | Не обязательно. Метод будет виден во всех модулях проекта, но не будет виден по ссылке на класс. то есть при конструкции Dim cl as new ExampleClass; Dim cll as object; Set cll=cl в объекте cll свойство с модификатором Friend видно не будет, а в объекте cl будет |
Static | Не обязательно. Значения объявленных локальных переменных внутри метода сохраняются между обращениями. Не распространяется на переменные, объявленные на уровне модуля класса |
аргументы | Не обязательно. По сути, это аргументы функции. Имена и типы аргументов должны совпадать с аргументами метода Property Get |
ссылка | Не обязательно. Имя переменной метода, которая будет ссылкой на объект. Тип данных ссылки должен быть object |
произвольный код | Не обязательно. Любое количество строк кода, например работа со свойствами объекта. И конечно необходимо здесь передать значение из локальной переменной (значение) метода во внешнюю переменную модуля класса. А значение внешней переменной передать в выражение метода Property Get |
Замечание:
— Не забывайте, передача ссылки на объект происходит при помощи Set.
Примеры создания свойства в модуле класса Pacient:
Private sHeight As Single Public Property Get Height() As Single Height = sHeight End Property Public Property Let Height(ByVal sHeightValue As Single) sHeight = sHeightValue End Property
Свойство Height доступно для чтения и записи. при этом при записи и чтении никаких модификаций с данными не производится, то есть такая конструкция аналогична открытому полю (Public Height as Single), а потому создание ее смысла не имеет. Просто лишний код.
Private sHeight As Single Public Property Get Height() As Single Height = sHeight End Property
Свойство доступно только для чтения. Метод Property Let не создавался. Предполагается, что данные переменной sHeight заданы в какой-либо процедуре модуля класса.
Const sHeight As Single = 2 Public Property Get Height() As Single Height = sHeight End Property
Свойство доступно только для чтения и содержит константу. Единственный способ открытия константы из объектного модуля. Конструкция Public Const sHeight As Single = 2 работать не будет.
Private sHeight As Single Public Property Let Height(ByVal sHeightValue As Single) sHeight = sHeightValue End Property
Свойство только для записи. при попытке его прочитать, появится ошибка.
Private sHeight As Single Public Property Get Height() As Single Height = sHeight End Property
Public Property Let Height(ByVal sHeightValue As Single) Select Case sHeightValue Case Is > 250 sHeight = 250 Case Is < 50 sHeight = 50 Case Else sHeight = sHeightValue End Select End Property
Происходит проверка данных при записи значения.
Private sHeight As Single Public Property Get Height() As Single Height = sHeight / 100 End Property Public Property Let Height(ByVal sHeightValue As Single) sHeight = sHeightValue End Property
Пример произвольного кода в методе Property Get. В свойство Height записывается значение в сантиметрах, а читается в метрах.
Private sHeight As Single Public Property Get Height(ByVal Scales As Integer) As Single Select Case Scales Case 1 Height = sHeight Case 2 Height = sHeight / 10 Case 3 Height = sHeight / 1000 End Select End Property Public Property Let Height(ByVal Scales As Integer, ByVal sHeightValue As Single) Select Case Scales Case 1 sHeight = sHeightValue Case 2 sHeight = sHeightValue * 10 Case 3 sHeight = sHeightValue * 1000 End Select End Property
Свойство со аргументом. Аргумент Scales указывает, в каких единицах измерения записывается рост, а в каких читается.
Private sHeight As Single Private sWeight As Single Public Property Let Height(ByVal sHeightValue As Single) sHeight = sHeightValue End Property Public Property Let Weight(ByVal sWeightValue As Single) sWeight = sWeightValue End Property Property Get IMT() As Single If sHeight <> 0 Then IMT = Round(sWeight / sHeight ^ 2, 1) End Property
Свойства Height и Weight только для записи, свойство IMT только для чтения и содержит индекс массы тела, рассчитанный на основе веса и роста. Расчет происходит всякий раз при обращении к свойству IMT.
Методы класса
В созданном классе можно создавать процедуры и функции. Они будут видны в экземпляре класса как методы, если указаны как Public и не видны, если указаны как Private. Все процедуры и функции, которые не планируется использовать как методы, должны быть Private. Этого требует принцип инкапсуляции.
Их создание ничем не отличается от создания процедур и функций в обычном модуле. Процедуры, как и функции, могут содержать обязательные и не обязательные аргументы. Однако вызов их отличается.
Код в классе | Вызов метод |
Public Function IMT(sWeight As Single, sHeight As Single) As Single If sHeight <> 0 Then IMT = Round(sWeight / sHeight ^ 2, 1) End Function |
Dim oPac As Pacient Set oPac = New Pacient Debug.Print oPac.IMT(113, 1.83) Set oPac = Nothing |
Public IMT As Single Public Sub CalcIMT(sWeight As Single, sHeight As Single) If sHeight <> 0 Then IMT = Round(sWeight / sHeight ^ 2, 1) End Sub |
Dim oPac As Pacient Set oPac = New Pacient oPac.CalcIMT sWeight:=113, sHeight:=1.83 Debug.Print oPac.IMT Set oPac = Nothing |
В первом случае значение индекса массы тела возвращает функция, во втором открытое поле IMT.
Классический вариант:
Код в классе | Вызов метод |
Private sHeight As Single Private sWeight As Single Private sIMT As Single Public Property Get Height() As Single Height = sHeight End Property Public Property Let Height(ByVal sHeightValue As Single) sHeight = sHeightValue End Property Public Property Get Weight() As Single Weight = sWeight End Property Public Property Let Weight(ByVal sWeightValue As Single) sWeight = sWeightValue End Property Public Property Get IMT() As Single IMT = sIMT End Property Public Sub CalcIMT() If sHeight <> 0 Then sIMT = Round(sWeight / sHeight ^ 2, 1) End Sub |
Dim oPac As Pacient Set oPac = New Pacient oPac.Height = 1.83 oPac.Weight = 113 oPac.CalcIMT Debug.Print oPac.IMT Set oPac = Nothing |
Свойства Height и Weight для чтения и записи, свойство IMT только для чтения. Индекс массы тела рассчитывается методом CalcIMT.
События класса
Созданный класс уже имеет два скрытых события:
Class_Initialize — Происходит при создании экземпляра класса. В этом событии удобно указывать значения свойств и переменных по-умолчанию.
Class_Terminate — Происходит при уничтожении экземпляра класса. Экземпляр класса уничтожается, когда процедура, в которой он был объявлен, завершает свою работу. Или после явной деинициализации экземпляра класса:
Set oPac=Nothing
Добавление собственных событий в класс, которые будут происходить при определенных условиях, не составляет особых сложностей. Единственное условие – экземпляр класса с событиями должен (может) быть объявлен только в объектном модуле (модуль класса, формы, листа, книги) на уровне модуля.
Private WithEvents oPac As Pacient
А в самом модуле класса указывается событие:
Синтаксис:
[Public] Event имя[(аргументы)]
Элемент | Описание |
Public | Не обязательно. Делает событие открытым во всех объектных модулях проекта. По-умолчанию, все пользовательские события Public |
имя | Имя события, которое будет видно в экземпляре класса |
аргументы | Не обязательно. События могут иметь аргументы, которым можно передавать значения или ссылку на объект |
Замечание:Событие должно указываться в самом начале модуля класса
В месте, где должно происходить событие указывается триггер с синтаксисом:
RaiseEvent имя[(значение аргументов)]
Элемент | Описание |
имя | Обязательно. Имя события, для которого указывается триггер |
значение аргументов | Не обязательно. Если при указании события были указаны аргументы, здесь через запятую указываются их значения |
Вот пример кода расчета индекса массы тела в форме.
Код в классе:
Public Event IMTCalculated(IMTValue As Single) Private sHeight As Single Private sWeight As Single Private sIMT As Single Public Property Get Height() As Single Height = sHeight End Property Public Property Let Height(ByVal sHeightValue As Single) sHeight = sHeightValue End Property Public Property Get Weight() As Single Weight = sWeight End Property Public Property Let Weight(ByVal sWeightValue As Single) sWeight = sWeightValue End Property Public Sub CalcIMT() If sHeight <> 0 Then sIMT = Round(sWeight / sHeight ^ 2, 1) RaiseEvent IMTCalculated(sIMT) End Sub
Вызов метода в форме:
Private WithEvents oPac As Pacient Private Sub CommandButton1_Click() Set oPac = New Pacient oPac.Height = 1.83 oPac.Weight = 113 oPac.CalcIMT End Sub Private Sub oPac_IMTCalculated(IMTValue As Single) Me.Label1.Caption = IMTValue End Sub
Событие IMTCalculated срабатывает после расчета индекса массы тела, и в форме значение аргумента IMTValue присваивается надписи. Таким образом, отпадает необходимость в создании отдельного свойства IMT.
Практическое применение модулей классов
Большинство разработчиков никогда не используют модули классов в своих программах. Но есть задачи, которые нельзя решить без написания пользовательских классов:
Есть случаи, когда использовать пользовательские классы удобнее, чем процедуры и функции:
Макросы – это полноценная программа, просто она выполняется на базе другого приложения. Следовательно, все принципы программирования соблюдаются. А все языки программирования разделяются на две категории: процедурные и объектно-ориентированные.
Макросы разрабатываются на базе языка VBA, который является скорее процедурным с возможностью обращения к отдельным объектам. Недостатком такого подхода есть то, что одна процедура не связана тесно с другой.
Объектно-ориентированный подход имеет некоторые принципиальные отличия от процедурного. Сейчас этот тип программирования является наиболее популярным. Поэтому его нужно рассмотреть более детально применительно к макросам Excel.
Содержание
- Общие сведения и определения
- Как создать класс в VBA
- Способы создания экземпляра класса
- Способ №1
- Способ №2
- Способ №3
- Способ №4
- Как удалить экземпляр класса?
- Как создать поля класса
- Процесс определения свойств класса
- Пример создания свойства в модуле класса
- Методы класса
- События класса
- Выводы
Общие сведения и определения
Все подпрограммы, которые есть в объектно-ориентированном программировании, тесно связаны между собой. Основное понятие, которое нужно знать на этом этапе – это класс.
Под классом подразумевается тот тип данных, в котором приводится описание структуры объекта. Все программы, написанные с помощью объектно-ориентированных языков, основываются на объектах. В случае с Excel такими служат формы, лист, книги, диаграммы и другие. Класс может объединять в себя целую кучу объектов, каждый из которых является своеобразной его копией с некоторыми доработками.
Выражаясь проще, класс – это шаблон, по которому генерируется объект.
Структура класса:
- Поле. Это такой элемент класса, в котором непосредственно содержатся данные.
- Свойство. Это составная часть класса, в которой также хранятся данные, которые в дальнейшем могут быть обработаны.
- Метод. По сути та же процедура или функция, просто в рамках класса. Методы – одно из главных понятий любого объектно-ориентированного языка программирования.
- Событие. Это ситуация, при которой активируется метод. Это может быть изменение содержимого ячеек или выполнение другого метода.
VBA не является полноценным объектно-ориентированным языком программирования, поскольку в нем есть только два принципа:
- Абстракция. С помощью этого метода можно создать объект, который больше всего напоминает некомпьютерный. Например, можно создать объект «собака», в котором будут такие свойства, как «количество ушей», «длина шерсти» и другие.
- Инкапсуляция. Она дает возможность спрятать класс от других процедур, которые не используют его в своей работе. Но этот принцип допускает использование отдельных свойств класса другими процедурами, если это требуется.
Как создать класс в VBA
Переходим к практическим аспектам использования классов в языке программирования VBA. Чтобы создать класс, в редакторе нужно нажать на пункт «Class Module», который можно найти в меню «Insert». Далее нужно классу дать определенное имя. Это можно сделать только одним способом – изменить имя, сгенерированное автоматически.
VBA предусматривает возможность спрятать класс от другой книги, если она ссылается на эту. За эту возможность отвечает свойство Instancing. Если его значение выставлено на Private, то этот класс будет обнаруживаться только той книгой, в которой макрос открыт. Если такой макрос открыт на нескольких документах, то класс будет доступен только той книге, к которой он привязан. Если же установить параметр PublicNotCreateable, то класс будет обнаруживаться в том случае, если он создан в книге, но все равно не будет доступен для других книг.
Способы создания экземпляра класса
Итак, под классом подразумевается описание объекта, но не сам объект. Чтобы на основе класса его создать, необходимо воспользоваться одним из доступных способов.
Способ №1
Вот строка кода, которая демонстрирует первый способ создания объекта на основе существующего класса.
Private Sub TestClass()
Dim cl As ExampleClass
Set cl = New ExampleClass End Sub
Способ №2
Dim cl As ExampleClass
Private Sub TestClass()
Set cl = New ExampleClass End Sub
В этом случае объявление класса осуществляется за пределами процедуры, поэтому работа с ним возможна со всех имеющихся внутри модуля процедур. Если же заменить оператор Dim на Public, то объект будет доступен любой процедуре, которая выполняется в рамках проекта при условии, что объявление осуществляется за пределами объектного модуля.
Способ №3
Dim cl WithEvents As ExampleClass
Private Sub TestClass()
Set cl = New ExampleClass End Sub
В данном примере объект создается с событиями, и только в связи с ними они становятся доступными. Этот метод имеет ограниченную сферу использования, его можно применять лишь в рамках объектного модуля (например, листа или книги).
Способ №4
Private Sub TestClass()
Dim cl As New ExampleClass
End Sub
Этот метод не является самым популярным, и многие про него вообще не знают. В этом случае создание объекта осуществляется через переменную cl. Этот метод несколько хуже, потому что сначала лучше выделить память под определенную переменную, и только потом создавать объект, который будет встроен в эту переменную.
Как удалить экземпляр класса?
Хорошая привычка – очистить выделенную память, если этот объект уже не нужен. Это позволит значительно увеличить быстродействие макроса. Особенно это важно, если он будет использоваться на слабом компьютере.
Чтобы это сделать, нужно воспользоваться следующей строкой кода.
Set cl = Nothing
Конечно, современные версии офисных пакетов (равно как и компьютеры) устроены так, что если проигнорировать эту рекомендацию и не удалять класс, то ничего страшного не случится. Тем не менее, настоятельно рекомендуется это делать на случай, если придется запускать макрос на слабом компьютере (особенно сейчас, когда популярность стал набирать класс устройств, который называется смартбуками).
Как создать поля класса
Созданный нами ранее класс не содержит ничего, ни полей, ни свойств, ни других описанных выше элементов. Следовательно, от него никакой пользы. Поэтому давайте начнем с создания полей класса. Это переменные, которые используются для хранения данных.
Поля бывают двух типов: открытые и закрытые. Основная характеристика последних заключается в том, что они являются недоступными для других процедур, которые не находятся внутри этого модуля. Доступ к открытым может иметь любой модуль и в этом аспекте они не отличаются принципиально от свойств.
Но отличаются другим – невозможно указать значение, которое будет отображаться в поле по умолчанию, а также невозможно запретить средствами VBA его редактирование. Информация в поле хранится до того момента, как она не будет перезаписана или же пока существует соответствующая переменная.
Чтобы создать поле, необходимо выполнить такой код.
Dim sBody As String
Также можно воспользоваться таким вариантом кода, который выдает тот же результат.
Private sBody As String
Оба описанных случая позволяют создать поле закрытого типа. Чтобы создать открытое поле, необходимо написать такую строчку.
Public Head As String
После этих манипуляций становится возможным использование свойства Head у объекта. С ним можно выполнять разные операции, включая запись:
cl.Head = «FHead»
и чтение
Debug.Print cl.Head
Тип данных для поля строго определен. Поэтому нельзя изменить его на числовой или формат числа с плавающей точкой.
Процесс определения свойств класса
Под свойством подразумевается также элемент, через который можно записывать и читать информацию объекта, но отличие заключается в том, что это функции, а не переменные.
Для чтения данных используется функция Property Get, а для записи – Property Let. Конечно, языком предусмотрена еще и третья конструкция –– Property Set, но на практике она используется редко из-за того, что ссылку на другой объект (а именно для этого и нужна указанная функция) можно записать и с помощью процедуры Property Let.
Давайте детальнее опишем синтаксис всех описанных функций. Итак, вот скриншот, описывающий синтаксис функции Property Get.
А вот таблица, в которой детально описываются элементы свойств класса.
При чтении свойства класса нужно учитывать такие нюансы:
- С помощью элемента Exit Property осуществляется выход из метода. Допустимо использование сразу нескольких подобных элементов в рамках одного метода. Это может быть полезным, например, если используются циклы и условные операторы.
- Несмотря на то, что сама функция Property Get может содержать неограниченное количество процедур внутри, она сама не может быть составной частью других функций.
Теперь давайте более подробно рассмотрим синтаксис функции Property Let. Он виден на этом скриншоте.
Этой функции касаются все замечания, рассмотренные выше.
Ну и наконец, как выглядит синтаксис функции Property Set. Несмотря на то, что она считается бесполезной, знать, как с ней работать нужно хотя бы потому, что другой человек может использовать именно ее. И нужно уметь читать чужой код. Сам синтаксис приводится на этом скриншоте.
Пример создания свойства в модуле класса
А теперь давайте детальнее рассмотрим некоторые практические примеры. Будем приводить фрагменты кода с краткими пояснениями.
В данном случае мы можем использовать свойство Height как для чтения, так и для того, чтобы редактировать информацию, которая там указана. Простыми словами, мы имеем фактически открытое поле, потому что имеет все его характеристики. Просто образуется оно немного другим методом – через функцию, а не переменную с процедурой.
Теперь давайте взглянем немного на другой фрагмент кода.
В этом случае мы не можем вносить никаких изменений в свойство, поскольку был открыт доступ только для чтения. По умолчанию берется идея, что значение переменной sHeight уже указано в другой процедуре, которая есть в данном модуле.
В этом примере свойство не может быть отредактировано, и в нем содержится постоянное значение (константа). Ее можно открыть только из модуля, который находится в данном объекте. Если мы попробуем использовать конструкцию Public Const sHeight As Single = 2, то у нас ничего не получится.
Существует еще множество примеров, но они уже более глубокого уровня и не подходят для новичков. Поэтому давайте сейчас рассмотрим особенности создания методов и событий класса.
Методы класса
В рамках класса можно создавать как процедуры, так и функции. Все они суммарно называются методами, но лишь при одном условии. Чтобы функция считалась методом, она должна быть открыта для других классов. Если же не требуется таких прав для подпрограмм, то нужно обязательно их делать приватными. Это обязательное правило, которое соответствует принципу инкапсуляции.
В целом, процедура создания методов ничем не отличается от того, как создаются процедуры и функции. В них могут содержаться любые аргументы как те, которые нужно обязательно указывать, так и те, в которых смысла нет. При этом отличается вызов. Внимательнее посмотрите на следующий пример.
События класса
Внутри каждого класса содержится уже несколько событий:
- Class_Initialize. Событие, которое осуществляется в момент создания класса. Здесь можно легко настроить значения переменных и свойств, которые будут использоваться всегда, если не указано обратное.
- Class_Terminate. Это событие происходит, когда объект удаляется. Это происходит в двух случаях. Первый – деинициализация объекта. Второй – прекращение работы той процедуры, в рамках которой был объявлен класс.
Как правило, не возникает никаких проблем с тем, чтобы добавлять новые события. При этом есть одно требование. Важно убедиться в том, что объект, к которому привязывается событие, объявлялся только в рамках одного модуля на этом же уровне.
Само же событие указывается в рамках самого модуля.
Синтаксис конструкции, с помощью которой происходит объявление события, следующий.
Выводы
Конечно, это нелегкая тема для понимания. Но это всего лишь два принципа объектно-ориентированного программирования. В случае же с другими языками все еще сложнее. VBA вообще-то считается очень легким языком. Поэтому если приложить должное упорство, все должно получиться. Успехов.
Оцените качество статьи. Нам важно ваше мнение:
The VBA Class allows you to define your own objects with unique properties and methods in VBA. Classes in at the core of all Object Oriented Programming languages. Although we can argue how much is VBA actually an Object Oriented Programming language, there is no doubt that VBA allows you to create Classes similarly as in Java or C#. But what is a Class? A Class allows you to encapsulate any level of abstraction and complexity into a single object and facilitate only an interface (certain procedures or methods if you prefer) to this object.
Personally I am a fan of (VBA) Classes as they require abstract thinking and a modular approach to writing code (in this case VBA). If you have seen a lot of VBA code, not once were you probably horrified to see a junkyard of random Subs and Functions with no obvious relationship between them. Classes whereas “require” that you encapsulate a certain “amount of functionality” into a single object which maps similarly to the world around us which is represented by single objects which can help us yield certain results. In my opinion this is an easy concept to grasp as everyday we are dealing with individual tools and objects that allow us to achieve certain goals. Sometime we are not necessary sure how these objects work (a PC) but we know how to use their interface to achieve an objective (send an email). Let’s use a simple analogy as we move forward through this tutorial…
VBA Class example
Before I explain more let us see a very simple Class example. To create a class insert a “Class” module to the “Class Modules” folder:
Next I inserted some example code to my class below and renamed my class (in the Properties panel) to MyClass.
Public name As String Public Sub Hello() Debug.Print "Hello my name is " & name End Sub
If you want to use your class you can create a simple VBA Sub in any module:
Sub Main() Dim class as New MyClass class.name = "John" class.Hello End Sub
Output:
Hello my name is John
The Car – Class Analogy
One of my personal favorite analogies of a Class is the Car Class. Let us say we want to create a Class for a Car object. Now an object of this class (a Car) will have certain properties like the brand of the Car, the color, the license plate numbers, as well as will have certain behaviors (called methods in Computer Programming) like driving forward, turning left, breaking etc. See below a simple example of how this translates to a VBA Class.
This approach makes it easier for someone who does not understand/know the underlying implementation to easily start using the Car Class object, as the Car object encapsulates everything that has to do with a single Car object the user can seamlessly reuse this object.
Creating a new VBA Class
Let’s start our journey by creating a new empty VBA Class. To add a new VBA Class go to the menu and select Insert then select Class Module.
Alternatively, right-click on any item in your VBA Project and select Class Module like shown below:
Classes in VBA are similar to regular VBA modules. They have their own namespace and can consist of procedures, functions, variables etc. There are other things you will find in a VBA Class, but we will get to that.
Next let’s make sure to name our Class appropriately:
Make sure that the Class name is not identical to a module or procedure within your VBA project or this will make it complicated to use this Class.
Now you have a new empty Class. It doesn’t do much except exist. But before we put some life into it let’s test that indeed it works. I have created a simple VBA Class which I named CarClass. The example below will create the new class.
Sub TestCarClass() Hello
Notice that when defining the Car variable I used the Set clause. This is because each Class is an object and the Car variable contains only a reference to this object. If you are not familiar with handling VBA Objects read here. Now let’s animate our class by adding some procedures (behaviors/methods) and some properties.
Variables, Procedures and Functions
A VBA Class can contain variables, procedures and functions just like regular VBA modules.
Let’s extend our VBA Class CarClass with some additional procedures and variables common to our abstract model of a car.
Public Speed As Integer Public LicensePlate as String Sub DriveForward() Speed = IIf(Speed < 0, -Speed, Speed) End Sub Sub DriveBack() Speed = IIf(Speed < 0, Speed, -Speed) End Sub
We can now create a new object of Class CarClass and start working with it:
Sub TestCarClass() Dim Car As CarClass Set Car = New CarClass Car.LicensePlate = "34344W" Car.Speed = 100 'set speed to 100 mph Car.DriveBack 'set speed to -100 mph Car.DriveForward 'set speed to 100 mph End Sub
Properties – Get, Let and Set
A Class should encapsulate abstraction and complexity. With our current CarClass we don’t have much control over the values users provide for the Speed and LicensePlate variables. What if the user sets the speed to some non-sense value like 1000mph? What if we want to validate the LicensePlate before setting it? Fortunately, the VBA Class introduces Get, Let and Set procedures that address that.
- Get – return value of the property
- Let – set the value of the property
- Set – set the object value of the property (if applies)
The Get and Let procedures manage the process of retrieving and defining the value of a specific Class property. The Set procedure is basically a Setter aimed at properties which are objects not native VBA data structures. Let look at another simple example with our CarClass:
Let’s replace this:
Public Speed As Integer Public LicensePlate as String
With this:
Dim vSpeed As Integer Dim vLicensePlate As String Public Property Get Speed() As Integer Speed = vSpeed End Property Public Property Let Speed(sp As Integer) vSpeed = Application.WorksheetFunction.Min(sp, 100) vSpeed = Application.WorksheetFunction.Max(vSpeed, -100) End Property Public Property Get LicensePlate() As String LicensePlate = vLicensePlate End Property Public Property Let LicensePlate(lp As String) If Len(lp) <> 6 Then Err.Raise (xlErrValue) 'Raise error vLicensePlate = lp End Property
Notice the highlighted rows above where I introduced the new Get and Let property procedures. As you can see now I am getting and setting the Speed and LicensePlate value via the property procedures. This is more convenient than making a variable Public within the class, as it gives you more control on the value of the variable. Remember that a Class should encapsulate abstraction and complexity. A perfect Class object prevents the user from any restricted behaviors with this object.
Notice that you use properties similarly as you would use Public variables:
Dim car As CarClass Set car = New CarClass car.Speed = 10 Debug.Print car.Speed '10 car.Speed = 12345 Debug.Print car.Speed '100 car.LicensePlate = "123456" Debug.Print car.LicensePlate '123456 car.LicensePlate = "1234567" 'Error!
Events – Initialize and Terminate
The Class module provides 2 default event procedures Class_Initialize() and Class_Terminate():
- Private Sub Class_Initialize() – fired when the Class object is initialized e.g.
Set car = New CarClass
- Private Sub Class_Terminate() – fired when the Class object is destroyed e.g.
Set car = Nothing
These 2 procedures are meant to facilitate the following objectives:
- Initializing the Class object to prepare it for use i.e. initializing objects associated with the Class like Collections, setting default value of Class variables
- Terminate the Class object and any objects associated with the Class i.e. free allocated memory
Let’s now extend our CarClass with a the following event procedures:
Dim SpeedRegister As Collection Private Sub Class_Initialize() Set SpeedRegister = New Collection vSpeed = 0 'Set speed to 0 vLicensePlate = "XXXXXX" 'Set unknown License Plate End Sub Private Sub Class_Terminate() Set SpeedRegister = Nothing End Sub
We will use the SpeedRegister to record any changes in the speed of our Car. See full code of our CarClass :
Dim vSpeed As Integer Dim vLicensePlate As String Dim SpeedRegister As Collection Public Property Get Speed() As Integer Speed = vSpeed End Property Public Property Let Speed(sp As Integer) vSpeed = Application.WorksheetFunction.Min(sp, 100) vSpeed = Application.WorksheetFunction.Max(vSpeed, -100) SpeedRegister.Add sp End Property Public Property Get LicensePlate() As String LicensePlate = vLicensePlate End Property Public Property Let LicensePlate(lp As String) If Len(lp) <> 6 Then Err.Raise (xlErrValue) 'Raise error vLicensePlate = lp End Property Sub DriveForward() Speed = IIf(Speed < 0, -Speed, Speed) End Sub Sub DriveBack() Speed = IIf(Speed < 0, Speed, -Speed) End Sub Private Sub Class_Initialize() Set SpeedRegister = New Collection End Sub Private Sub Class_Terminate() Set SpeedRegister = Nothing End Sub
Instancing – Private vs. PublicNonCreatable
VBA In the properties section of the VBA Class you will find a property call Instancing. You are probably wondering what this property does. There are two available options – Private and PublicNonCreatable:
- Private – the Class cannot be created and referenced outside the VBA Project. It is “Private” to the VBA Project
- PublicNonCreatable – the Class is Public and can be referenced out the VBA Project. It cannot be however created outside the project using the New clause
But why would you want to set your Class to PublicNonCreatable? Whenever you want to use it outside your VBA Project e.g. from a separate Excel, Access, MS Office VBA Project file currently open. This is especially useful if you have a Class you want to use within multiple VBA Projects and not having to copy the Class code to every project. You can consider creating a simple Excel AddIn and include the Class code in the VBA Project. If the AddIn is placed in your C:usernameAppDataRoamingMicrosoftAddIns directory it will open automatically with your MS Office applications.
But how are we supposed to use this Class if we can create this object? We need to create a dedicated function that will return the object. Let’s make an effort now to reuse our CarClass Class within a separate VBA Project:
Add a new Module and add a function
First we need to use an existing or create a new Module in which we will place our function which initializes the CarClass :
Public Function New_CarClass() Set New_CarClass = New CarClass End Function
Notice that the Class needs to be Public so it can be referenced from the other VBA Project.
Set the Class to PublicNonCreatable
Click on the Class (CarClass) and change Instancing from Private to PublicNonCreatable.
Create the Class using the defined function
You are ready to reuse the CarClass in a different VBA Project:
Dim car as Object Set car = New_CarClass 'Use the Class normally...
That’s it!
VBA Class Module vs. Standard Module
What is the difference between a standard Module and a Class Module? A Class instance is basically and Object and has all the properties of Objects in VBA. Here are some key differences worth highlighting:
- There is only one copy of standard module data. A Class module, however, is created separately for each instance of the class. Hence each Class instance has it’s own set of Class variables individual to the object
- Data in a standard module exists as long as your program scope – the life of you VBA Project. Only when you close your Excel/Access/Word/PowerPoint file will the data be lost for all global variables. In a Class module, however, data for each instance exists only for the lifetime of the object. It ceases to exist the moment the Class Object is destroyed e.g. when the Class object is set to Nothing
- Variables declared Public in a standard module are visible anywhere in your project. However, Public variables in a Class module are only accessed if you have an object variable containing a reference to a particular instance of a class
Conclusions
Hopefully this tutorial has familiarized you with the VBA Class enough to start using it. As mentioned I think it is really worth making the effort to learn the VBA Class as using classes makes your code more readable and manageable. Classes encapsulate abstraction and complexity – it makes life easier when you or others need to reuse your code without necessarily needing to review the code and implementation.
If you feel you mastered Classes in Excel macros do check out my VBA Implements Tutorial as well!
Let me know what you think! Be sure to follow me on Twitter and Facebook for upcoming posts and tutorials!
На чтение 24 мин. Просмотров 22.7k.
Председатель Мао
Классовая борьба, одни классы побеждают, другие исключаются. Такова история
Содержание
- Краткое руководство по VBA Class Module
- Введение
- Почему мы используем объекты
- Преимущества использования объектов
- Недостатки использования объектов
- Создание простого Class Module
- Class Module VBA против обычных модулей VBA
- Части Class Module
- События Class Module
- Class Module. Пример 1
- Class Module. Пример 2
- Заключение
Краткое руководство по VBA Class Module
Пункт | Пояснение |
Class Module | Позволяет пользователю создавать свои собственные объекты. |
Метод | Открытая функция или подпрограмма в Class Module . |
Переменная | Переменная, объявленная в Class Module. |
Свойство | Специальная функция / подпрограммы, которые ведут себя как переменные при использовании |
Типы свойств | Get, Let и Set. |
Событие — инициализация | Sub, который автоматически запускается при создании объекта Class Module. |
Событие — завершение | Sub, который автоматически запускается при удалении объекта Class Module. |
Объявление и создание — статический |
Dim o As New Class1 |
Объявление и создание — динамический |
Dim o As Class1 Set o = New Class1 |
Вызов подпрограммы Class Module |
o.WriteValues Total |
Вызов функции Class Module | Amount = o.Calculate() |
Использование свойства Class Module |
o.Amount = 1 Total = o.Amount |
Введение
Class Module VBA позволяют пользователю создавать свои собственные объекты. Если вы не знакомы с объектами, я настоятельно рекомендую вам сначала ознакомиться с моей статьей Все об объектах VBA Objects.
В таких языках, как C # и Java, классы используются для создания объектов. Class Module являются VBA-эквивалентом этих классов. Основное отличие состоит в том, что Class Module VBA имеют очень ограниченный тип наследования * по сравнению с классами на других языках. В VBA наследование работает аналогично интерфейсам ** в C # Java.
В VBA у нас есть встроенные объекты, такие как Collection, Workbook, Worksheet и так далее. Целью Class Module VBA является предоставление нам возможности создавать собственные объекты.
Давайте начнем эту статью с рассмотрения того, почему мы используем объекты в первую очередь.
*Наследование использует существующий класс для создания нового класса.
**Интерфейсы — это форма наследования, которая заставляет класс реализовывать специфические процедуры или свойства.
Почему мы используем объекты
Использование объектов позволяет нам создавать наши приложения так же, как мы используем строительные блоки.
Идея состоит в том, что код каждого объекта является автономным. Он полностью независим от любого другого кода в нашем приложении.
Это похоже на то, как все строится с использованием кирпичей Lego. Существует много различных типов компонентов Lego. Например, блок, руль и лазер — это разные вещи. Они ведут себя совершенно независимо друг от друга. Но мы можем соединить их вместе, чтобы создать здание, транспортное средство, космическую станцию и так далее.
Если вам все еще неясно, не волнуйтесь. В остальной части этой статьи мы разберем все это на простые термины.
Преимущества использования объектов
Рассматривая части нашего кода как блоки, мы получаем много больших преимуществ.
- Это позволяет нам создавать приложения по одному блоку за раз.
- Намного проще протестировать отдельные части приложения.
- Обновление кода не вызовет проблем в других частях приложения.
- Легко добавлять объекты между приложениями.
Недостатки использования объектов
У большинства вещей в жизни есть свои плюсы и минусы. Использование Class Module VBA ничем не отличается. Ниже приведены недостатки использования Class Module для создания объектов.
- Первоначально для создания приложений требуется больше времени *.
- Не всегда легко четко определить, что такое объект.
- Люди, плохо знакомые с классами и предметами, могут сначала найти их трудными для понимания.
*Если вы создаете приложение с использованием объектов, на его создание уйдет больше времени, поскольку вам придется тратить больше времени на планирование и проектирование. Однако в долгосрочной перспективе это сэкономит вам огромное количество времени. Вашим кодом будет легче управлять, обновлять и использовать повторно.
Создание простого Class Module
Давайте рассмотрим очень простой пример создания Class Module и использования его в нашем коде.
Чтобы создать Class Module, мы щелкаем правой кнопкой мыши в окне Project и затем выбираем Insert и Class Module.
Наш новый класс называется Class1. Мы можем изменить имя в окне свойств, как показано на следующем скриншоте.
Давайте изменим имя модуля класса на clsCustomer. Затем мы добавим переменную в Class Module следующим образом.
Теперь мы можем использовать этот Class Module в любом модуле (стандартном или классе) в нашей рабочей книге. Например:
' Создать объект из Class Module Dim oCustomer As New clsCustomer ' Установите имя клиента oCustomer.Name = "Иван" ' Напечатайте имя в Immediate Window(Ctrl + G) Debug.Print oCustomer.Name
Class Module против Объектов
Люди, которые плохо знакомы с использованием классов и модулей классов VBA, часто путаются между тем, что такое класс и что является объектом.
Давайте посмотрим на пример из реального мира. Думайте о предмете массового производства как кофейная кружка. Дизайн кружки создается в первую очередь. Затем тысячи кофейных кружек создаются из этого дизайна.
Это похоже на работу Class Module и объектов.
Class Module можно рассматривать как дизайн.
Объект можно рассматривать как элемент, созданный из дизайна.
Ключевое слово New в VBA — это то, что мы используем для создания объекта из Class Module. Например:
' Создание объектов с использованием New Dim oItem As New Class1 Dim oCustomer1 As New clsCustomer Dim coll As New Collection
Примечание. Мы не используем New для таких элементов, как Workbooks и Worksheets. См. Когда New не требуется для получения дополнительной информации.
Class Module VBA против обычных модулей VBA
Написание кода в Class Module почти такое же, как написание кода в обычном модуле. Мы можем использовать тот же код, который мы используем в обычных модулях. То, как этот код используется, сильно отличается.
Давайте посмотрим на два основных различия между классом и обычным модулем. Это часто вызывает путаницу у новых пользователей.
Разница 1 — Как используются модули
Если вы хотите использовать подпрограмму / функцию и т.д. Из
Class Module, вы должны сначала создать объект.
Например, представьте, что у нас есть два идентичных Sub PrintCustomer. Один находится в Class Module, а другой — в обычном модуле…
' CLASS MODULE Код - clsCustomer Public Sub PrintCustomer() Debug.Print "Пример вывода" End Sub
' Код обычного модуля Public Sub PrintCustomer() Debug.Print "Пример вывода" End Sub
Вы заметите, что коды абсолютно одинаковые.
Чтобы использовать подпрограмму PrintCustomer из Class Module, вы должны сначала создать объект этого типа
' Другой модуль Sub UseCustomer() Dim oCust As New clsCustomer oCust.PrintCustomer End Sub
Чтобы использовать PrintCustomer из обычного модуля, вы можете вызвать его напрямую
' Другой модуль Sub UseCustomer() PrintCustomer End Sub
Разница 2 — Количество копий
Когда вы создаете переменную в обычном модуле, существует только одна ее копия. Для Class Module существует одна копия переменной для каждого создаваемого вами объекта.
Например, представьте, что мы создаем переменную StudentName как в классе, так и в обычном модуле.
' Обычный модуль Public StudentName As String
' CLASS MODULE Public StudentName As String
Для обычной переменной модуля в нашем приложении будет только одна копия этой переменной.
Для Class Module новая копия переменной StudentName создается каждый раз, когда создается новый объект.
Dim student1 As New clsStudent Dim student2 As New clsStudent student1.StudentName = "Петр" student2.StudentName = "Василий"
Когда вы полностью поймете Class Module VBA, эти различия будут казаться очевидными.
Части Class Module
В Class Module есть четыре разных предмета. Это:
- Методы — функции / подводные лодки.
- Переменные-члены — переменные.
- Свойства — типы функций / подпрограмм, которые ведут себя как переменные.
- События — подводные лодки, которые запускаются событием
Вы можете видеть, что они все или функции, подпрограммы или переменные.
Давайте кратко рассмотрим некоторые примеры, прежде чем разбираться с ними по очереди.
' CLASS MODULE Код ' Переменная Private dBalance As Double ' Свойства Property Get Balance() As Double Balance = dBalance End Property Property Let Balance(dValue As Double) dBalance = dValue End Property ' Событие - срабатывает при создании класса Private Sub Class_Initialize() dBalance = 100 End Sub ' Методы Public Sub Withdraw(dAmount As Double) dBalance = dBalance - dAmount End Sub Public Sub Deposit(dAmount As Double) dBalance = dBalance + dAmount End Sub
Теперь, когда мы увидели примеры, давайте рассмотрим каждый из них по очереди.
Методы Class Module
Методы относятся к процедурам класса. В VBA есть процедуры и функции. Как и переменные-члены, они могут быть Public или Private.
Давайте посмотрим на пример:
' CLASS MODULE Код ' Имя класса: clsSimple ' Публичные процедуры могут быть вызваны извне объекта Public Sub PrintText(sText As String) Debug.Print sText End Sub Public Function Calculate(dAmount As Double) As Double Calculate = dAmount - GetDeduction End Function ' частные процедуры могут быть вызваны только из Class Module Private Function GetDeduction() As Double GetDeduction = 2.78 End Function
Мы можем использовать Class Module clsSimple следующим образом
Sub ClassMembers() Dim oSimple As New clsSimple oSimple.PrintText "Hello" Dim dTotal As Double dTotal = oSimple.Calculate(22.44) Debug.Print dTotal End Sub
Переменные-члены Class Module
Переменная-член очень похожа на обычную переменную, которую мы используем в VBA. Разница в том, что мы используем Public или Private вместо Dim.
' CLASS MODULE Код Private Balance As Double Public AccountID As String
Примечание: Dim и Private делают одно и то же, но соглашение заключается в том, чтобы использовать Dim в sub / functions и использовать Private за пределами sub / functions.
Ключевое слово Public означает, что переменная может быть доступна вне Class Module. Например:
Dim oAccount As New clsAccount ' Действительный - AccountID открыт oAccount.AccountID = "499789" ' Ошибка - Баланс является частным oAccount.Balance = 678.90
В приведенном выше примере мы не можем получить доступ к Балансу, потому что он объявлен, как Частный. Мы можем использовать только приватную переменную внутри Class Module. Мы можем использовать функцию / подпрограмму в Class Module, например:
' CLASS MODULE Код Private Balance As Double Public Sub SetBalance() Balance = 100 Debug.Print Balance End Sub
Считается плохой практикой иметь публичные переменные-члены. Это потому, что вы позволяете коду вне объекта мешать работе класса. Цель использования классов состоит в том, чтобы скрыть происходящее от вызывающего.
Чтобы пользователь не общался напрямую с нашими переменными-членами, мы используем Свойства.
Свойства Class Module
- Get — возвращает объект или значение из класса
- Let — устанавливает значение в классе
- Set — устанавливает объект в классе
Формат свойств VBA
Обычный формат для свойств выглядит следующим образом:
Public Property Get () As Type End Property Public Property Let (varname As Type ) End Property Public Property Set (varname As Type ) End Property
Мы уже видели, что свойство это просто тип sub. Назначение свойства — позволить вызывающей стороне получать и устанавливать значения.
Почему мы используем свойства
Почему мы не можем просто сделать переменные общедоступными и использовать их напрямую?
Давайте объясним с некоторыми примерами. Представьте, что у нас есть класс, который ведет список стран. Мы могли бы сохранить список в виде массива:
' Использовать массив для хранения стран Public arrCountries As Variant ' Установить размер массива при инициализации класса Private Sub Class_Initialize() ReDim arrCountries(1 To 1000) End Sub
Когда пользователь хочет получить количество стран в списке, он может сделать это:
' Код обычного модуля Dim oCountry As New clsCountry ' Получить количество элементов NumCountries = UBound(oCountry.arrCountries) + 1
С приведенным выше кодом есть две основные проблемы
- Чтобы узнать количество стран, вам необходимо знать, как хранится список, например, Массив.
- Если мы изменим массив на коллекцию, нам нужно будет изменить весь код, который напрямую ссылается на массив
Для решения этих проблем мы можем создать функцию, возвращающую количество стран:
' CLASS MODULE Код - clsCountryList ' Массив Private arrCountries() As String Public Function Count() As Long Count = UBound(arrCountries) + 1 End Function
Затем мы используем это так
' Код модуля Dim oCountries As New clsCountries Debug.Print "Количество стран " & oCountries.Count
Этот код решает две проблемы, которые мы перечислили выше. Мы можем изменить наш массив на коллекцию, и код вызывающего абонента все равно будет работать. Например:
' CLASS MODULE Код ' Коллекция Private collCountries() As Collection Public Function Count() As Long Count = collCountries.Count End Function
Вызывающий не замечает, как хранятся страны. Все, что нужно знать вызывающему — это то, что функция Count будет возвращать количество стран.
Как мы только что видели, подпрограмма или функция обеспечивает решение вышеуказанных проблем. Однако использование свойства может обеспечить более элегантное решение.
Использование свойства вместо Function/Sub
Вместо создания функции Count мы можем создать свойство Count. Как вы можете видеть ниже, они очень похожи:
' Замени это Public Function Count() As Long Count = UBound(arrCountries) + 1 End Function ' На это Property Get Count() As Long Count = UBound(arrCountries) + 1 End Function
В этом сценарии нет большой разницы между использованием свойства и использованием функции. Тем не менее, есть различия. Обычно мы создаем свойство Get и Let так:
' CLASS MODULE Код - clsAccount Private dTotalCost As Double Property Get TotalCost() As Long TotalCost= dTotalCost End Property Property Let TotalCost(dValue As Long) dTotalCost = dValue End Property
Использование Let позволяет нам рассматривать свойство, как переменную. Таким образом, мы можем сделать это:
Второе отличие состоит в том, что использование Let и Get позволяет нам использовать одно и то же имя при обращении к свойству Get или Let. Таким образом, мы можем использовать свойство, как переменную. Это цель использования свойств над подпрограммой и функцией.
oAccount.TotalCost = 6 dValue = oAccount.TotalCost
Если мы использовали функцию и подпрограмму, то мы не можем получить поведение переменной. Вместо этого мы должны вызвать две разные процедуры, например:
oAccount.SetTotalCost 6 dValue = oAccount.GetTotalCost
Вы также можете видеть, что когда мы использовали Let, мы можем присвоить значение, как переменную. Когда мы используем SetTotalCost, мы должны были передать его в качестве параметра.
О Свойствах в двух словах
- Свойство скрывает детали реализации от вызывающей стороны.
- Свойство позволяет нам обеспечивать то же поведение, что и переменная.
Типы свойств VBA
Есть три типа свойств. Мы уже видели Get и Let. Но мы еще не рассмотрели Set.
Set похож на Let, но он используется для объекта (подробнее об этом см. Назначение объектов VBA).
Первоначально в Visual Basic ключевое слово Let использовалось для назначения переменной. На самом деле, мы можем использовать его, как захотим.
' Эти строки эквивалентны Let a = 7 a = 7
Поэтому мы используем Let, чтобы присвоить значение переменной, и мы используем Set, чтобы назначить объект переменной объекта.
' Используем Let Dim a As Long Let a = 7 ' Используем Set Dim coll1 As Collection, coll2 As Collection Set coll1 = New Collection Set coll2 = coll1
- Let используется для присвоения значения базовому типу переменной.
- Set используется для назначения объекта переменной объекта
В следующем примере мы используем свойства Get и Let для строковой переменной
' CLASS MODULE Код ' Свойства SET/LET для переменной Private m_sName As String ' свойства Get/Let Property Get Name() As String Name = m_sName End Property Property Let Name(sName As String) m_sName = sName End Property
Затем мы можем использовать свойства Name так:
Sub TestLetSet() Dim sName As String Dim coll As New Collection Dim oCurrency As New clsCurrency ' Свойство Let oCurrency.Name = "USD" ' Свойство Get sName = oCurrency.Name End Sub
В следующем примере мы используем свойства Get и Set для переменной объекта
' CLASS MODULE Код Private m_collPrices As Collection ' Свойства Get/Set Property Get Prices() As Collection Set Prices = m_collPrices End Property Property Set Prices(collPrices As Collection) Set m_collPrices = collPrices End Property
Затем мы можем использовать свойства так:
Sub TestLetSet() Dim coll1 As New Collection Dim oCurrency As New clsCurrency ' Свойство Set Set oCurrency.Prices = coll1 ' Свойство Get Dim coll2 As Collection Set Coll2 = oCurrency.Prices End Sub
Мы используем свойство Get, чтобы вернуть значения для обоих элементов. Обратите внимание, что даже если мы используем свойство Get для возврата коллекции, нам все равно нужно использовать ключевое слово Set для его назначения.
События Class Module
Class Module имеет два события:
- Инициализировать — происходит при создании нового объекта класса.
- Завершить — происходит, когда объект класса удален.
В объектно-ориентированных языках, таких как C ++, эти события называются Конструктором и Деструктором. В большинстве языков вы можете передавать параметры конструктору, но не в VBA. Мы можем использовать Class Factory, чтобы обойти эту проблему, как показано ниже.
Инициализация
Давайте создадим очень простой Class Module с именем clsSimple с событиями Initialize и Terminate.
' CLASS MODULE Код Private Sub Class_Initialize() MsgBox "Класс инициализируется" End Sub Private Sub Class_Terminate() MsgBox "Класс прекращается" End Sub Public Sub PrintHello() Debug.Print "Привет" End Sub
В следующем примере мы используем Dim и New для создания объекта.
В этом случае oSimple не создается, пока мы не ссылаемся на него в первый раз, например:
Sub ClassEventsInit2() Dim oSimple As New clsSimple ' Инициализация происходит здесь oSimple.PrintHello End Sub
Когда мы используем Set и New вместе, поведение отличается. В этом случае объект создается при использовании Set, например:
Sub ClassEventsInit() Dim oSimple As clsSimple ' Инициализация происходит здесь Set oSimple = New clsSimple oSimple.PrintHello End Sub
Примечание: Для получения дополнительной информации о различиях между использованием New с Dim и использованием New с Set см. Тонкие различия Dim и Set
Как я уже говорил ранее, вы не можете передать параметр в Initialize. Если вам нужно сделать это, вам нужна функция, чтобы сначала создать объект.
' CLASS MODULE - clsSimple Public Sub Init(Price As Double) End Sub ' обычный модуль Public Sub Test() ' использование функции CreateSimpleObject Dim oSimple As clsSimple Set oSimple = CreateSimpleObject(199.99) End Sub Public Function CreateSimpleObject(Price As Double) As clsSimple Dim oSimple As New clsSimple oSimple.Init Price Set CreateSimpleObject = oSimple End Function
Мы расширим CreateSimpleObject в Примере 2, чтобы создать фабрику классов.
Завершение
Событие Terminate наступает при удалении класса. Это происходит, когда мы устанавливаем значение Nothing.
Sub ClassEventsTerm() Dim oSimple As clsSimple Set oSimple = New clsSimple ' Завершение происходит здесь Set oSimple = Nothing End Sub
Если мы не установим объект в Nothing, VBA автоматически удалит его, когда он выйдет из области видимости.
Это означает, что если мы создадим объект в процедуре, когда эта процедура завершится, VBA удалит все созданные объекты.
Sub ClassEventsTerm2() Dim oSimple As New clsSimple ' Инициализация происходит здесь oSimple.PrintHello ' oSimple удаляется, когда мы выходим из этого Sub-вызова Terminate End Sub
Class Module. Пример 1
В этом примере мы рассмотрим очень распространенное использование Class Module.
Представьте, что у нас есть следующие данные:
Мы хотим читать альбомы по разным годам, а затем создавать различные отчеты.
Мы могли бы использовать для этого 2D-массив или коллекцию коллекций, например:
For i = 2 To rg.Rows.Count Year = rg.Cells(i, 3) If startYear <= Year And endYear >= Year Then ' Создать новую коллекцию для каждой строки Set rowColl = New Collect ' Добавить исполнителя rowColl.Add rg.Cells(i, 1).Value ' Добавить заголовок rowColl.Add rg.Cells(i, 2).Value ' и так далее ' Добавить коллекцию строк в основную коллекцию coll.Add rowColl End If Next i
Как вы можете себе представить, этот код очень быстро запутался.
К счастью для нас, у нас есть Class Module VBA, чтобы сделать нашу жизнь проще. Мы можем создать Class Module для хранения элементов.
' clsAlbum class module Private m_sArtist As String Private m_sTitle As String Private m_sYear As String Private m_sGenre As String Private m_sSales As String ' Свойства Public Property Get Artist() As String Artist = m_sArtist End Property Public Property Let Artist(ByVal sArtist As String) m_sArtist = sArtist End Property ' и т.д.
Каждый раз, когда мы хотим добавить запись, мы можем сделать это следующим образом:
' Объявить переменную Dim oAlbum As clsAlbum ' Создать новый альбом Set oAlbum = New clsAlbum ' Добавить детали oAlbum.Artist = rg.Cells(i, 1) oAlbum.Title = rg.Cells(i, 2) oAlbum.Year = rg.Cells(i, 3) oAlbum.Genre = rg.Cells(i, 4) oAlbum.Sales = rg.Cells(i, 5) ' Добавить объект альбома в коллекцию coll.Add oAlbum
Как видите, это делает наш код более читабельным. Понятно, для чего используются Artist, Title и т.д.
Затем мы можем легко использовать эти данные для создания отчетов, записи в файлы и т.д.
Sub PrintAlbum(coll As Collection) Dim oAlbum As clsAlbum For Each oAlbum In coll ' Распечатайте название и исполнителя для каждого альбома Debug.Print oAlbum.Title, oAlbum.Artist Next End Sub
Ниже приведен полный код для этого примера
Sub CreateReport() Dim coll As Collection ' читать данные Set coll = ReadAlbums(1990, 2001) ' Распечатать информацию об альбоме PrintAlbum coll ' Распечатать общий объем продаж PrintTotalSales coll End Sub Function ReadAlbums(startYear As Long, endYear As Long) _ As Collection Dim rg As Range Set rg = Sheet1.Range("A1").CurrentRegion ' Создать коллекцию для хранения альбомов Dim coll As New Collection Dim oAlbum As clsAlbum Dim i As Long, Year As Long For i = 2 To rg.Rows.Count Year = rg.Cells(i, 3) If startYear <= Year And endYear >= Year Then ' Создать новый альбом Set oAlbum = New clsAlbum ' Добавить детали oAlbum.Artist = rg.Cells(i, 1) oAlbum.Title = rg.Cells(i, 2) oAlbum.Year = Year oAlbum.Genre = rg.Cells(i, 4) oAlbum.sales = rg.Cells(i, 5) ' Добавить объект альбома в коллекцию coll.Add oAlbum End If Next i Set ReadAlbums = coll End Function Sub PrintAlbum(coll As Collection) Dim oAlbum As clsAlbum For Each oAlbum In coll Debug.Print oAlbum.Title, oAlbum.Artist Next End Sub Sub PrintTotalSales(coll As Collection) Dim oAlbum As clsAlbum, sales As Double For Each oAlbum In coll sales = sales + oAlbum.sales Next Debug.Print "Общее количество продаж составляет " & sales End Sub
Class Module. Пример 2
В этом примере мы пойдем дальше. Мы собираемся взглянуть на некоторые хитрые приемы при использовании объектов.
Представьте, что у вас есть список продуктов, как на картинке ниже.
Продукты имеют разные поля, поэтому нам нужно использовать разные модули классов для каждого типа продуктов. Один тип для строки Книги, один тип для строки Фильмы.
Сначала мы создадим наши модули классов. Они очень похожи для обоих типов продуктов.
' CLASS MODULE - clsBook ' Переменные Private m_Title As String Private m_Year As Long ' Свойства Property Get ItemType() As String ItemType = "Book" End Property Property Get Title() As String Title = m_Title End Property Property Get Year() As Long Year = m_Year End Property ' Методы Public Sub Init(rg As Range) m_Title = rg.Cells(1, 2) m_Year = CLng(rg.Cells(1, 4)) End Sub Public Sub PrintToImmediate() Debug.Print ItemType, m_Title, m_Year End Sub
' CLASS MODULE - clsFilm ' Переменные Private m_Title As String Private m_Year As Long ' Свойства Property Get ItemType() As String ItemType = "Film" End Property Property Get Title() As String Title = m_Title End Property Property Get Year() As Long Year = m_Year End Property ' Методы Sub Init(rg As Range) m_Title = rg.Cells(1, 2) m_Year = CLng(rg.Cells(1, 5)) End Sub Public Sub PrintToImmediate() Debug.Print ItemType, m_Title, m_Year End Sub
Как видите, единственная реальная разница — это инициализация.
Когда мы читаем каждую запись, нам нужно определить, книга это или фильм. Затем мы создаем соответствующий объект. Представьте, что нам нужно создать переменную для каждого типа, например:
' Для каждого типа требуется одна переменная Dim oBook As clsBook Dim oFilm As clsFilm ' Если Книга сделать это Set oBook = New clsBook ' Если фильм сделать это Set oFilm = New clsFilm
Если бы у нас было много разных типов, это было бы действительно очень грязно. Хорошей новостью является то, что нам нужно использовать только одну переменную!
В VBA мы можем объявить переменную как вариант. Когда мы используем Variant, мы, по сути, говорим: «Мы определим тип переменной во время выполнения кода».
Это очень полезно при работе с объектами и позволяет нам избежать использования одной переменной, например:
' Требуется только одна переменная Dim oItem As Variant ' Если книга, указать тип clsBook Set oItem = New clsBook ' Если фильм, указать тип clsFilm Set oItem = New clsFilm
Это действительно полезно, так как нам нужна только одна переменная, независимо от того, сколько у нас объектов.
Второе преимущество использования Variant заключается в следующем. Если у каждого Class Module есть подпрограмма / функция с одинаковым именем и параметрами, мы можем использовать одну и ту же переменную для ее вызова.
Представьте, что в clsBook есть функция InitBook, а в clsFilm есть функция InitFilm. Нам нужно сделать это:
' Если clsBook If Type = "Book" Then oItem.InitBook ElseIf Type = "Film" Then oItem.InitFilm
Однако, если они имеют одинаковое имя, например, Init, мы можем заменить строки кода If ElseIf одной строкой:
' это вызовет подпрограмму Init любого типа oItem, установленного в oItem.Init
Теперь мы можем создать функцию для создания соответствующего объекта. В объектно-ориентированном программировании мы имеем то, что называется фабрикой классов. Это просто функция, которая создает объект на основе заданного типа.
Ранее мы видели, что событие Initialize не принимает параметры. Мы можем позвонить в Init на фабрике классов, чтобы обойти эту проблему.
Полный код для функции ClassFactory здесь:
Function ClassFactory(rg As Range) As Variant ' Получить тип продукта Dim sType As String sType = rg.Cells(1, 1) ' Создать объект на основе типа Dim oItem As Variant Select Case sType Case "Book": Set oItem = New clsBook Case "Film": Set oItem = New clsFilm Case Else MsgBox "Invalid type" End Select ' Разобрать поля на правильные переменные класса oItem.Init rg ' Вернуть объект продукта Set ClassFactory = oItem End Function
Это следующее наше начало. В этом разделе мы читаем таблицу и передаем диапазон в ClassFactory.
Создает объект, передает диапазон в метод Parse объекта. Затем он возвращает объект, который мы добавляем в нашу коллекцию.
Sub ReadProducts() ' Создать коллекцию Dim coll As New Collection Dim product As Variant Dim rg As Range ' Читайте продукты с листа Dim i As Long For i = 1 To 2 Set rg = Sheet1.Range("A" & i & ":E" & i) Set product = ClassFactory(rg) coll.Add product Next ' Распечатать информацию о продукте в Immediate Window(Ctrl + G) PrintCollection coll End Sub
Мы также можем использовать вариант объекта для печати элементов. Пока оба объекта имеют подпрограмму с одинаковым именем и параметрами (например, PrintToImmediate), мы можем вызывать ее, используя тип Variant.
Public Sub PrintCollection(ByRef coll As Collection) Dim v As Variant For Each v In coll ' Печать элементов v.PrintToImmediate Next End Sub
Заключение
На этом я заканчиваю свою статью о Class Module VBA. В этой статье мы рассмотрели части Class Module VBA и два примера, в которых вы могли бы их использовать.
Важно понимать, что классы и объекты — это обширная тема. Существует множество типов объектов, которые вы можете создавать, и способы их использования.
Если вы планируете использовать Class Module, то я советую начать с основ и ознакомиться с тем, как создать простой. Как только вы освоите основы, вам будет намного легче переходить к более сложным сценариям.
“Classes struggle, some classes triumph, others are eliminated. Such is history” – Chairman Mao
A Quick Guide to the VBA Class Module
Item | Explanation |
---|---|
Class Module | Allows the user to create their own objects. |
Method | A public function or sub in the class module. |
Member variable | A variable declared in the class module. |
Property | Special function/subs that behave like variables when used |
Property types | Get, Set and Let. |
Event — Initialize | Sub that automatically runs when the class module object is created. |
Event — Terminate | Sub that automatically runs when the class module object is deleted. |
Declaring and Creating — Static |
Dim o As New Class1 |
Declaring and Creating — Dynamic | Dim o As Class1 Set o = New Class1 |
Calling a class module sub | o.WriteValues Total |
Calling a class module function | Amount = o.Calculate() |
Using a class module property | o.Amount = 1 Total = o.Amount |
The Webinar
Members of the Webinar Archives can access the webinar for this article by clicking on the image below.
(Note: Archive members have access to the webinar archive.)
Introduction
VBA Class Modules allow the user to create their own objects. If you are not familiar with objects then I would highly recommend that you first check out my previous post VBA Objects – The Ultimate Guide.
In languages such as C# and Java, classes are used to create objects. Class Modules are the VBA equivalent of these classes. The major difference is that VBA Class Modules have a very limited type of Inheritance* compared to classes in the other languages. In VBA, Inheritance works in a similar way to Interfaces** in C#Java.
In VBA we have built-in objects such as the Collection, Workbook, Worksheet and so on. The purpose of VBA Class Modules is to allow us to custom build our own objects.
Let’s start this post by looking at why we use objects in the first place.
*Inheritance is using an existing class to build a new class.
**Interfaces are a form of Inheritance that forces a class to implement specifics procedures or properties.
Download the Source Code
Why Do We Use Objects
Using objects allows us to build our applications like we are using building blocks.
The idea is that the code of each object is self-contained. It is completely independent of any other code in our application.
This is similar to how things are built using Lego® bricks. There are many different types of Lego® components used. For example, a block, steering wheel, and laser are different items. They behave completely independently of each other. The wheel spins, the laser rotates etc. Yet we can connect them together to create a building, vehicle, space station and so on.
If you are still not clear about this then don’t worry. We’ll be breaking it all down into simple terms in the rest of this post.
Advantages of Using Objects
Treating parts of our code as blocks provide us with a lot of great advantages
- It allows us to build an application one block at a time.
- It is much easier to test individual parts of an application.
- Updating code won’t cause problems in other parts of the application.
- It is easy to add objects between applications.
Not a good look for your code © BigStockPhoto.com
Disadvantages of Using Objects
With most things in life there are pros and cons. Using VBA class modules is no different. The following are the disadvantages of using class module to create objects
- It takes more time initially to build applications*.
- It is not always easy to clearly define what an object is.
- People new to classes and objects can find them difficult to understand at first.
*If you create an application using objects it will take longer to create it initially as you have to spend more time planning and designing it. However, in the long run it will save you a huge amount of time. Your code will be easier to manage, update and reuse.
Creating a Simple Class Module
If you would like to see working examples of this code you can download the source code from the top of this post.
Let’s look at a very simple example of creating a class module and using it in our code.
To create a class module we right-click in the Project window and then select Insert and Class Module
Adding a Class Module
Our new class is called Class1. We can change the name in the Properties window as the following screenshot shows:
Let’s change the name of the class module to clsCustomer. Then we will add a variable to the class module like this:
Public Name As String
We can now use this class module in any module(standard or class) in our workbook. For example
' Create the object from the class module Dim oCustomer As New clsCustomer ' Set the customer name oCustomer.Name = "John" ' Print the name to the Immediate Window(Ctrl + G) Debug.Print oCustomer.Name
Class Module versus Objects
People who are new to using classes and VBA class modules, often get confused between what is a class and what is an object.
Let’s look at a real world example. Think of a mass produced item like a coffee mug. A design of the mug is created first. Then, thousands of coffee mugs are created from this design.
This is similar to how class modules and objects work.
The class module can be thought of as the design.
The object can be thought of as the item that is created from the design.
The New keyword in VBA is what we use to create an object from a class module. For example:
' Creating objects using new Dim oItem As New Class1 Dim oCustomer1 As New clsCustomer Dim coll As New Collection
Note: We don’t use New with items such as Workbooks and Worksheets. See When New is not required for more information.
VBA Class Modules Versus VBA Normal Modules
Writing code in a class module is almost the same as writing code in a normal module. We can use the same code we use in normal modules. It’s how this code is used which is very different.
Let’s look at the two main differences between the class and the normal module. These often cause confusion among new users.
Difference 1 – How the modules are used
If you want to use a sub/function etc. from a class module you must create the object first.
For example, imagine we have two identical PrintCustomer subs. One is in a class module and one is in a normal module…
' CLASS MODULE CODE - clsCustomer Public Sub PrintCustomer() Debug.Print "Sample Output" End Sub
' NORMAL MODULE CODE Public Sub PrintCustomer() Debug.Print "Sample Output" End Sub
You will notice the code for both is exactly the same.
To use the PrintCustomer sub from the class module, you must first create an object of that type
' Other Module ' https://excelmacromastery.com/ Sub UseCustomer() Dim oCust As New clsCustomer oCust.PrintCustomer End Sub
To use PrintCustomer from the normal module you can call it directly
' Other Module ' https://excelmacromastery.com/ Sub UseCustomer() PrintCustomer End Sub
Difference 2 – Number of copies
When you create a variable in a normal module there is only one copy of it. For a class module, there is one copy of the variable for each object you create.
For example, imagine we create a variable StudentName in both a class and normal module:
' NORMAL MODULE Public StudentName As String
' CLASS MODULE called clsStudent Public StudentName As String
For the normal module variable there will only be one copy of this variable in our application.
StudentName = "John"
For the class module, a new copy of the variable StudentName is created each time a new object is created.
Dim student1 As New clsStudent Dim student2 As New clsStudent student1.StudentName = "Bill" student2.StudentName = "Ted"
When you fully understand VBA class modules, these differences will seem obvious.
The Parts of a Class Module
There are four different items in a class module. These are
- Methods – functions/subs.
- Member variables – variables.
- Properties– types of functions/subs that behave like variables.
- Events – subs that are triggered by an event.
You can see they are all either functions, subs or variables.
Let’s have a quick look at class that has examples of each of these:
' CLASS MODULE CODE from clsAccount ' https://excelmacromastery.com/vba-class-modules/ ' Member variable Private m_balance As Double ' Properties Property Get Balance() As Double Balance = m_balance End Property Property Let Balance(value As Double) m_balance = value End Property ' Event - triggered when class created Private Sub Class_Initialize() m_balance = 100 End Sub ' Methods Public Sub Withdraw(amount As Double) m_balance = m_balance - amount End Sub Public Sub Deposit(amount As Double) m_balance = m_balance + amount End Sub
The following code demonstrates how this class could be used:
' This sub uses the clsAccount class ' The results are printed to the Immediate Window(Ctrl + G) ' https://excelmacromastery.com/vba-class-modules/ Sub Demo_clsAccount() Dim oAccount As New clsAccount ' Print the balance Debug.Print "Starting balance is: " & oAccount.Balance ' Deposit money oAccount.Deposit 25 ' Print the balance Debug.Print "Balance after deposit is: " & oAccount.Balance ' Withdraw Money oAccount.Withdraw 100 ' Print the balance Debug.Print "Balance after withdrawl is: " & oAccount.Balance End Sub
If we run the code we will get the following:
Starting balance is: 100
Balance after deposit is: 125
Balance after withdrawl is: 25
Now that we have seen examples, let’s take a look at each of these in turn.
Class Module Methods
Methods refer to the procedures of the class. In VBA procedures are subs and functions. Like member variables they can be Public or Private.
Let’s look at an example:
' CLASS MODULE CODE for clsExample ' https://excelmacromastery.com/vba-class-modules/ ' Public procedures can be called from outside the object Public Sub PrintText(text As String) Debug.Print text End Sub Public Function Calculate(amount As Double) As Double Calculate = amount - GetDeduction End Function ' private procedures can only be called from within the Class Module Private Function GetDeduction() As Double GetDeduction = 2.78 End Function
We can use the clsExample class module like this:
' Sub used to demonstrate Class clsExample ' https://excelmacromastery.com/vba-class-modules/ Public Sub ClassMembers() Dim oSimple As New clsExample oSimple.PrintText "Hello" Dim total As Double total = oSimple.Calculate(22.44) Debug.Print total End Sub
Class Module Member Variables
The member variable is very similar to the normal variable we use in VBA. The difference is we use Public or Private instead of Dim.
' CLASS MODULE CODE Private Balance As Double Public AccountID As String
Note: Dim and Private do exactly the same thing but the convention is to use Dim in sub/functions and to use Private outside sub/functions.
The Public keyword means the variable can be accessed from outside the class module. For example:
' This code will give an ERROR!! Sub Demo_BankAccount() Dim oAccount As New clsBankAccount ' Valid - AccountID is public oAccount.AccountID = "499789" ' ERROR - Balance is private oAccount.Balance = 678.9 End Sub
In the above example, we cannot access Balance because it is declared as Private. We can only use a Private variable within the class module. We can use in a function/sub in the class module e.g.
' CLASS MODULE CODE ' https://excelmacromastery.com/ Private Balance As Double Public Sub SetBalance() Balance = 100 Debug.Print Balance End Sub
It is considered poor practice to have public member variables. This is because you are allowing code outside the object to interfere with how the class works. The purpose of the using classes is so that we hide what is happening from the caller.
To avoid the user directly talking to our member variables we use Properties.
Class Module Properties
- Get – returns an object or value from the class
- Let – sets a value in the class
- Set – sets an object in the class
Format of VBA Property
The normal format for the properties are as follows:
Public Property Get () As Type End Property Public Property Let (varname As Type ) End Property Public Property Set (varname As Type ) End Property
We have seen already that the Property is simply a type of sub. The purpose of the Property is to allow the caller to get and set values.
Why we use Properties
Why can’t we just make the variables Public and use them directly?
Let’s explain with some examples. Imagine we have a class that maintains a list of Countries. We could store the list as an array
' Use array to store countries ' https://excelmacromastery.com/ Public arrCountries As Variant ' Set size of array when class is initialized ' https://excelmacromastery.com/ Private Sub Class_Initialize() ReDim arrCountries(1 To 1000) End Sub
When the user wants to get the number of countries in the list they could do this:
' NORMAL MODULE CODE Dim oCountry As New clsCountry ' Get the number of items NumCountries = UBound(oCountry.arrCountries) - LBound(oCountry.arrCountries) + 1
There are two major problems with the above code:
- To get the number of countries you need to know how the list is stored e.g. Array.
- If we change the Array to a Collection, we need to change all code that reference the array directly.
To solve these problems we can create a function to return the number of countries
' CLASS MODULE CODE - clsCountryList ' Array Private arrCountries() As String Public Function Count() As Long Count = UBound(arrCountries) + 1 End Function
We then use it like this:
' MODULE CODE Dim oCountries As New clsCountries Debug.Print "Number of countries is " & oCountries.Count
This code solves the two problems we listed above. We can change our Array to a Collection and the caller code will still work e.g.
' CLASS MODULE CODE ' Collection ' https://excelmacromastery.com/ Private collCountries() As Collection Public Function Count() As Long Count = collCountries.Count End Function
The caller is oblivious to how the countries are stored. All the caller needs to know is that the Count function will return the number of countries.
As we have just seen, a sub or function provides a solution to the above problems. However, using a Property can provide a more elegant solution.
Using a Property instead of a Function/Sub
Instead of the creating a Count Function we can create a Count Property. As you can see below they are very similar:
' Replace this Public Function Count() As Long Count = UBound(m_countries) - LBound(m_countries) + 1 End Function ' With this Property Get Count() As Long Count = UBound(m_countries) - LBound(m_countries) + 1 End Property
In this scenario, there is not a lot of difference between using the Property and using a function. However, there are differences. We normally create a Get and Let property like this:
' https://excelmacromastery.com/vba-class-modules/ Private m_totalCost As Double Property Get totalCost() As Long totalCost = m_totalCost End Property Property Let totalCost(value As Long) m_totalCost = value End Property
Using Let allows us to treat the property like a variable. So we can do this
oAccount.TotalCost = 6
The second difference is that using Let and Get allows us to use the same name when referencing the Get or Let property. So we can use the property like a variable. This is the purpose of using Properties over a sub and function.
oAccount.TotalCost = 6 value = oAccount.TotalCost
If we used a function and a sub then we cannot get the behaviour of a variable. Instead we have to call two different procedures e.g.
oAccount.SetTotalCost 6 value = oAccount.GetTotalCost
You can also see that when we used Let we can assign the value like a variable. When we use SetTotalCost , we had to pass it as a parameter.
The Property in a Nutshell
- The Property hides the details of the implementation from the caller.
- The Property allows us to provide the same behaviour as a variable.
Types of VBA Property
There are three types of Properties. We have seen Get and Let already. The one we haven’t looked at is Set.
Set is similar to Let but it is used for an object(see Assigning VBA Objects for more detail about this).
Originally in Visual Basic, the Let keyword was used to assign a variable. In fact, we can still use it if we like.
' These line are equivalent Let a = 7 a = 7
So we use Let to assign a value to a variable and we use Set to assign an object to an object variable.
' Using Let Dim a As Long Let a = 7 ' Using Set Dim coll1 As Collection, coll2 As Collection Set coll1 = New Collection Set coll2 = coll1
- Let is used to assign a value to a basic variable type.
- Set is used to assign an object to an object variable.
In the following example, we use Get and Let properties for a string variable:
' CLASS MODULE CODE for clsPerson ' https://excelmacromastery.com/vba-class-modules/ ' SET/LET PROPERTIES for a variable Private m_name As String ' Get/Let Properties Property Get name() As String name = m_name End Property Property Let name(name As String) m_name = name End Property
We can then use the name properties like this:
' Testing Let and Set for the clsPerson Class ' https://excelmacromastery.com/vba-class-modules/ Sub TestLetSet() Dim name As String Dim oPerson As New clsPerson ' Let Property oPerson.name = "Bill" ' Get Property name = oPerson.name End Sub
In the next example, we use Get and Set properties for an object variable:
' CLASS MODULE CODE for clsCurrency ' https://excelmacromastery.com/vba-class-modules/ Private m_Prices As Collection ' Get/Set Properties Property Get Prices() As Collection Set Prices = m_Prices End Property Property Set Prices(newPrices As Collection) Set m_Prices = newPrices End Property
We can then use the properties like this:
' The code below demonstrates how to use the LetSet properties with a class. ' ' 1. The sub creates a collection and adds value. ' 2. We then add it to the clsCurrency class object using the Set Property. ' 3. We then read it from the class object usiing the Get property. ' ' https://excelmacromastery.com/vba-class-modules/ Sub TestLetSet() ' Create a collection and add prices Dim Prices As New Collection Prices.Add 21.23 Prices.Add 22.12 Prices.Add 20.12 Dim oCurrency As New clsCurrency ' Uses the Set property of clsCurrency to ' add the collection to the class Set oCurrency.Prices = Prices Dim PricesCopy As Collection ' Uses the Get property of clsCurrency ' to read the collection from the class Set PricesCopy = oCurrency.Prices ' Print the results to the Immediate Window(Ctrl + G) PrintCollection Prices, "Prices" PrintCollection PricesCopy, "Copy" End Sub ' Print the contents of a Collection to the Immediate Window(Ctrl + G) Sub PrintCollection(c As Collection, name As String) Debug.Print vbNewLine & "Printing " & name & ":" Dim item As Variant For Each item In c Debug.Print item Next item End Sub
We use the Get property to return the values for both items. Notice that even though we use the Get Property to return the Collection, we still need to use the Set keyword to assign it.
One very important thing to understand is that when we use Set we are still referencing the same collection. Set is not creating a copy of the collection. You can read more about this here
Class Module Events
If you to see working examples of this code you can download the source code from the top of this post.
A class module has two events
- Initialize – occurs when a new object of the class is created.
- Terminate – occurrs when the class object is deleted.
In Object-Oriented languages like C++, these events are referred to as the Constructor and the Destructor. In most languages, you can pass parameters to a constructor but in VBA you cannot. We can use a Class Factory to get around this issue as we will see below.
Initialize
Let’s create a very simple class module called clsSimple with Initialize and Terminate events:
' CLASS MODULE CODE ' https://excelmacromastery.com/ Private Sub Class_Initialize() MsgBox "Class is being initialized" End Sub Private Sub Class_Terminate() MsgBox "Class is being terminated" End Sub Public Sub PrintHello() Debug.Print "Hello" End Sub
In the following example, we use Dim and New to create the object.
In this case, oSimple is not created until we reference it for the first time e.g.
' https://excelmacromastery.com/ Sub ClassEventsInit2() Dim oSimple As New clsSimple ' Initialize occurs here oSimple.PrintHello End Sub
When we use Set and New together the behaviour is different. In this case the object is created when Set is used e.g.
' https://excelmacromastery.com/ Sub ClassEventsInit() Dim oSimple As clsSimple ' Initialize occurs here Set oSimple = New clsSimple oSimple.PrintHello End Sub
Note: For more information about the different between using New with Dim and using New with Set see Subtle Differences of Dim Versus Set
As I said earlier, you cannot pass a parameter to Initialize. If you need to do this you need a function to create the object first
' CLASS MODULE - clsSimple ' https://excelmacromastery.com/ Public Sub Init(Price As Double) End Sub ' NORMAL MODULE Public Sub Test() ' Use CreateSimpleObject function Dim oSimple As clsSimple Set oSimple = CreateSimpleObject(199.99) End Sub Public Function CreateSimpleObject(Price As Double) As clsSimple Dim oSimple As New clsSimple oSimple.Init Price Set CreateSimpleObject = oSimple End Function
We will expand on this CreateSimpleObject in Example 2 to create a Class Factory.
Terminate
The Terminate event occurs when the class is deleted. This happens when we set it to Nothing
' https://excelmacromastery.com/ Sub ClassEventsTerm() Dim oSimple As clsSimple Set oSimple = New clsSimple ' Terminate occurs here Set oSimple = Nothing End Sub
If we don’t set the object to Nothing then VBA will automatically delete it when it goes out of scope.
What this means is that if we create an object in a procedure, when that procedure ends VBA will delete any objects that were created.
' https://excelmacromastery.com/ Sub ClassEventsTerm2() Dim oSimple As New clsSimple ' Initialize occurs here oSimple.PrintHello ' oSimple is deleted when we exit this Sub calling Terminate End Sub
Class Module Example 1
In this example, we are going to look at a very common use of a Class module.
Imagine we have the following data
We want to read the Albums based on a range of years and then create various reports.
We could use a 2D Array for this or a Collection of collections e.g.
' https://excelmacromastery.com/ For i = 2 To rg.Rows.Count Year = rg.Cells(i, 3) If startYear <= Year And endYear >= Year Then ' Create a new collection for each row Set rowColl = New Collect ' Add artist rowColl.Add rg.Cells(i, 1).Value ' Add Title rowColl.Add rg.Cells(i, 2).Value ' and so on ' Add row collection to main collection coll.Add rowColl End If Next i
As you can imagine this code would get messy very quickly.
© BigStockPhoto.com
Lucky for us we have VBA class modules to make our life easier. We can create a class module to store the items.
' clsAlbum class module Private m_sArtist As String Private m_sTitle As String Private m_sYear As String Private m_sGenre As String Private m_sSales As String ' Properties ' https://excelmacromastery.com/ Public Property Get Artist() As String Artist = m_sArtist End Property Public Property Let Artist(ByVal sArtist As String) m_sArtist = sArtist End Property ' etc
Each time we want to add a record we can do it as follows:
' Declare the Variable Dim oAlbum As clsAlbum ' Create new album Set oAlbum = New clsAlbum ' Add the details oAlbum.Artist = rg.Cells(i, 1) oAlbum.Title = rg.Cells(i, 2) oAlbum.Year = rg.Cells(i, 3) oAlbum.Genre = rg.Cells(i, 4) oAlbum.Sales = rg.Cells(i, 5) ' Add the album object to the collection coll.Add oAlbum
You can see that this makes our code much more readable. It is clear what Artist, Title etc. are being used for.
We can then easily use this data to create reports, write to files etc.
' https://excelmacromastery.com/ Sub PrintAlbum(coll As Collection) Dim oAlbum As clsAlbum For Each oAlbum In coll ' Print out the title and artist for each album Debug.Print oAlbum.Title, oAlbum.Artist Next End Sub
Below is the full code for this example:
' https://excelmacromastery.com/ Sub CreateReport() Dim coll As Collection ' read the data Set coll = ReadAlbums(1990, 2001) ' Print the album details PrintAlbum coll ' Print the total sales PrintTotalSales coll End Sub Function ReadAlbums(startYear As Long, endYear As Long) _ As Collection Dim rg As Range Set rg = Sheet1.Range("A1").CurrentRegion ' Create a collection to store the albums Dim coll As New Collection Dim oAlbum As clsAlbum Dim i As Long, Year As Long For i = 2 To rg.Rows.Count Year = rg.Cells(i, 3) If startYear <= Year And endYear >= Year Then ' Create new album Set oAlbum = New clsAlbum ' Add the details oAlbum.Artist = rg.Cells(i, 1) oAlbum.Title = rg.Cells(i, 2) oAlbum.Year = Year oAlbum.Genre = rg.Cells(i, 4) oAlbum.sales = rg.Cells(i, 5) ' Add the album objecdt to the collection coll.Add oAlbum End If Next i Set ReadAlbums = coll End Function Sub PrintAlbum(coll As Collection) Dim oAlbum As clsAlbum For Each oAlbum In coll Debug.Print oAlbum.Title, oAlbum.Artist Next End Sub Sub PrintTotalSales(coll As Collection) Dim oAlbum As clsAlbum, sales As Double For Each oAlbum In coll sales = sales + oAlbum.sales Next Debug.Print "Total number sales is " & sales End Sub
Class Module Example 2
In this example, we’re going to take things a bit further. We’re going to look at some neat tricks when using objects.
Imagine you have a list of products like in the image below.
The products have different fields so we need to use a different class module for each product type. One type for a Book row, one type for a Film row.
We’ll create our class modules first. As you can imagine the are very similar for both product types
' CLASS MODULE - clsBook ' https://excelmacromastery.com/ ' Member variables Private m_Title As String Private m_Year As Long ' Properties Property Get ItemType() As String ItemType = "Book" End Property Property Get Title() As String Title = m_Title End Property Property Get Year() As Long Year = m_Year End Property ' Methods Public Sub Init(rg As Range) m_Title = rg.Cells(1, 2) m_Year = CLng(rg.Cells(1, 4)) End Sub Public Sub PrintToImmediate() Debug.Print ItemType, m_Title, m_Year End Sub
' CLASS MODULE - clsFilm ' https://excelmacromastery.com/ ' Member variables Private m_Title As String Private m_Year As Long ' Properties Property Get ItemType() As String ItemType = "Film" End Property Property Get Title() As String Title = m_Title End Property Property Get Year() As Long Year = m_Year End Property ' Methods Sub Init(rg As Range) m_Title = rg.Cells(1, 2) m_Year = CLng(rg.Cells(1, 5)) End Sub Public Sub PrintToImmediate() Debug.Print ItemType, m_Title, m_Year End Sub
As you can see, the only real difference is the Init sub.
When we read each record we need to determine if it is a Book or Film. Then we create the appropriate object. You would imagine we would have to create a variable for each type e.g.
' One variable required for each type Dim oBook As clsBook Dim oFilm As clsFilm ' If book do this Set oBook = New clsBook ' Else If film do this Set oFilm = New clsFilm
If we had lots of different types this would get very messy indeed. The good news is we only need to use one variable!
In VBA we can declare a variable as a Variant. When we use a Variant we are essentially saying “We will decide the type of variable when the code is running”.
This is very useful when dealing with objects and allows us to get away with using one variable e.g.
' Only one variable required Dim oItem As Variant ' If book set type to clsBook Set oItem = New clsBook ' Else If film set type to clsFilm Set oItem = New clsFilm
This is really useful as we only need one variable no matter how many objects we have.
A second advantage of using a Variant is this. If each Class Module has a sub/function with the same name and parameters, we can use the same variable to call it
So imagine clsBook has a function called InitBook and clsFilm has a function called InitFilm. We would need to do this:
' If clsBook If Type = "Book" Then oItem.InitBook ElseIf Type = "Film" Then oItem.InitFilm
However, if they have the same name, e.g. Init, we can replace the IfElseIf lines of code with one line:
' this will call the Init sub of whatever type oItem is set to
oItem.Init
We can now create a function to create the appropriate object. In Object Oriented Programming, we have what is called a Class Factory. This is simply a function that creates an object based on a given type.
We saw earlier that the Initialize event does not take parameters. We can call Init in the Class Factory to get around this issue.
The full code for the ClassFactory function is here:
' https://excelmacromastery.com/ Function ClassFactory(rg As Range) As Variant ' Get product type Dim sType As String sType = rg.Cells(1, 1) ' Create an object based on the type Dim oItem As Variant Select Case sType Case "Book": Set oItem = New clsBook Case "Film": Set oItem = New clsFilm Case Else MsgBox "Invalid type" End Select ' Parse the fields to the correct class variables oItem.Init rg ' Return the product object Set ClassFactory = oItem End Function
This following is our starting sub. In this sub, we read through the worksheet and pass the range to ClassFactory.
It creates the object, passes the range to the object Parse method. Then it returns the object which we add to our Collection.
' https://excelmacromastery.com/ Sub ReadProducts() ' Create the collection Dim coll As New Collection Dim product As Variant Dim rg As Range ' Read products from the worksheet Dim i As Long For i = 1 To 2 Set rg = Sheet1.Range("A" & i & ":E" & i) Set product = ClassFactory(rg) coll.Add product Next ' Print the product details to the Immediate Window(Ctrl + G) PrintCollection coll End Sub
We can also use the variant object to print the items. As long as both objects have a sub with the same name and parameters(e.g PrintToImmediate) we can call it using a Variant type.
' https://excelmacromastery.com/ Public Sub PrintCollection(ByRef coll As Collection) Dim v As Variant For Each v In coll ' Print items v.PrintToImmediate Next End Sub
The source code for this post also includes this example using Inheritance(i.e. class interfaces). You can download the code here:
Conclusion
That concludes my post on the VBA Class Modules. In this post, we have looked at the parts of the VBA Class Module and two example cases where you would use them.
It’s important to understand that Classes and Objects is a vast topic. There are countless types of objects you can create and ways you can use them.
If you plan to use Class Modules then my advice is to start simple and get familiar with how to create a simple one. Once you have mastered the basics it will be much easier to move onto more challenging scenarios.
What’s Next?
Free VBA Tutorial If you are new to VBA or you want to sharpen your existing VBA skills then why not try out the The Ultimate VBA Tutorial.
Related Training: Get full access to the Excel VBA training webinars and all the tutorials.
(NOTE: Planning to build or manage a VBA Application? Learn how to build 10 Excel VBA applications from scratch.)
Про объекты.
Очень часто, программированием на VBA занимаются непрофессиональные программисты и про ООП (Объектно-ориентированные программирование) слышали, кое-что знают, но сами никогда не создавали.
Наступает момент, когда написанные процедуры в модулях уже не справляются со своими задачами, удобнее создавать свои объекты.
Для того, чтобы на VBA создать свой объект, сначала надо написать класс модуля (Class Module).
Создаём простой класс в VBA это ClassModule. Сразу присвоим ему имя myClass. В дальнейшем это имя будет использоваться в качестве имени класса.
Давайте создадим одно простое свойство класса
Public Zametka as String
Пусть это будет текстовое значение.
Добавим к нашему классу одно действие
Public Sub Show() MsgBox Zametka End Sub
Пусть наш класс будет выводить значение заметки.
Для начала хватит. Теперь будем тестировать наш класс.
Переходим в модуль и создаём процедуру.
Sub TestMyObject 'Объявляем переменную Dim o As myClass 'Присваиваем переменной новый объект Set o = New myClass 'Присваиваем значение свойство нашего объекта o.Zametka = "Hello world!" 'Выполняем действие объекта o.Show End Sub
Ну вот и все. На экран выводится сообщение “Hello world!”
Мы рассмотрели простейший пример класса объекта на VBA.
Главное замечание. VBA ну очень медленно работает с объектами. Поэтому в коде стараемся минимизировать количество объектов.
Далее рассмотрим более сложные классы.
Excel VBA Class Module
VBA class allows us to create our Object function in which we can add any features, details of the command line, and type of function. So, when we create a class in VBA, they act like independent object functions, but they are all connected.
It helps us build applications already there in VBA and Excel. For example, the pedaling cycle wheel rotates. Pedals and wheels are the cycle parts, but both work independently to give the output as a moving cycle.
Table of contents
- Excel VBA Class Module
- How to Create Custom Class & Objects in VBA?
- Pros
- Cons
- Things to Remember
- Recommended Articles
You are free to use this image on your website, templates, etc, Please provide us with an attribution linkArticle Link to be Hyperlinked
For eg:
Source: VBA Class (wallstreetmojo.com)
How to Create Custom Class & Objects in VBA?
You can download this VBA Class Excel Template here – VBA Class Excel Template
Let us consider an example of three different companies’ mobile phones: Apple, Samsung, and Nokia —considering the popular and newly launched mobile phones of these companies, which are the iPhone X, Samsung S8, and Nokia 7+.
We will compare some of the important features of these mobile phones, such as brand, model, screen size, camera type, and charger type. These are important features on that basis. However, we mostly compare any mobile phone. Below we have mapped the above-discussed parameters in a chart.
In the VBA window from the Insert menu, select the Class Module, as shown below.
We will get the class module window starting with Option ExplicitVBA option explicitly makes a user mandatory to declare all the variables before using them; any undefined variable will throw an error while coding execution. We can enable it for all codes from options to require variable declaration.read more, as shown below.
Option Explicit ensures that it must declare variables before using. If we do not declare any variable and use it, the system will throw an error. Now, in class, define all the discussed parameters of measurement of mobile phones with the Public. Then, it will make those defined parameters open to use anytime, anywhere, without limitations.
Code:
Option Explicit 'List of properties Public Brand As String Public Model As String Public ScreenSize As String Public CameraType As String Public ChargerType As String
Now, we will add different operating, features, and functions of a mobile phone, such as starting a phone, Switching off the phone, playing music, charging the battery, etc., with a subcategory for each function as shown below. And add a message box in each subcategory loop so we can see which methods are currently running.
Code:
'Possible Techniques Sub MobileStarts() MsgBox "Mobile is Turning On" End Sub
Sub MobileOff() MsgBox "Mobile is Turning Off" End Sub
Sub PlayMusic() MsgBox "Audio system is currently working" End Sub
Sub BatteryCharge() MsgBox "Charger is currently plugged-in" End Sub
It completes the creation of the class. However, changing the class module’s name is advisable before we move further. Choose any name as per your requirement as we change it to Mobile.
Now, we will write a code in a module where we will see and compare the features and functions of each mobile phone brand. As shown, first, we need to add a Module under the “Insert” menu.
We will open a new module with Option Explicit enabled as we work and create a class. Then, write a subcategory in the name of performed functions, as shown below.
We can also change the module’s name as we did for class. It helps us keep the co-related identity of the created VBA codeVBA code refers to a set of instructions written by the user in the Visual Basic Applications programming language on a Visual Basic Editor (VBE) to perform a specific task.read more. It can be done from properties windows, as shown below.
We have already defined various features, functions, and methods of all brand mobile phones. Now, let us define three variables in any name (preferably in the name of the mobile phone brand) and assign them to Mobile. Let us start with the iPhone first, as shown below.
Similarly, do the same for the other mobile phone brands, as shown below.
By this, we assign the created Class to each dimensional variable of the Mobile brand. Now for each brand, do the same process of assigning the Class.
Now, we need to assign all the features of the Mobile phone and its value. First, use the Set function and assign it to a New Mobile, as shown below.
Now, open the “With-End” loop for iPhone. It is where we will define each attribute of the mobile.
As we can see, we have assigned all the defined features of the iPhone brand from class with specific values as String.
Do the same thing for Samsung and Nokia brands as well.
Now we will use DebugPrint to print the information on the prompt window. It is useful where we want to see a certain variable in a line of code. First, we will select different features for each Mobile brand, as shown below.
Now, assign the MobileStarts and MobileOff functional operations, which we defined in the class, to each mobile brand in the same module along with the message box. You may skip the message box here.
It completes the assigning of classes to the module. Now, compile the code and run using the F5 key. Finally, we will see the message of each mobile brand, as shown below.
Now, if you want to know what variables and functions have what kind of values in them, we must open the Locals window from the “View” tab, which will help us get the details when we compile the code, as shown below.
The below code is for your Reference.
Code:
Sub VBA_Class() Dim iPhone As Mobile Dim Samsung As Mobile Dim Nokia As Mobile Set iPhone = New Mobile With iPhone .Brand = "iPhone" .Model = "iPhone X" .ScreenSize = "6.5 Inches" .CameraType = "12 MegaPixel" .ChargerType = "Regular" End With Set Samsung = New Mobile With Samsung .Brand = "Samsung" .Model = "Samsung S8" .ScreenSize = "5.8 Inches" .CameraType = "12 MegaPixel" .ChargerType = "Power" End With Set Nokia = New Mobile With Nokia .Brand = "Nokia" .Model = "Nokia 7+" .ScreenSize = "6 Inches" .CameraType = "12 MegaPixel" .ChargerType = "Power" End With Debug.Print "Phone Screen Size is: " & iPhone.ScreenSize Debug.Print "Camera of Samsung is: " & Samsung.CameraType Debug.Print "Charger Type is: " & Nokia.ChargerType iPhone.MobileStarts iPhone.MobileOff MsgBox iPhone.Model Samsung.MobileStarts Samsung.MobileOff MsgBox Samsung.Model Nokia.MobileStarts Nokia.MobileOff MsgBox Nokia.Model End Sub
Pros
- We can build our application with a lot of features.
- Once we create the class, we can update any feature whenever we require it.
- If we update the code, it won’t cause any problems in other parts of the class.
- We can test the individual part of the application as per our needs.
Cons
- Initially, it takes a lot of time to create a class in VBA.
- New to VBA may find the class very difficult to apply.
Things to Remember
- As the code can be huge, it is better to compile it step-by-step. By doing this, we would avoid the number of errors, which will be difficult for us to debug and resolve.
- We can ignore the use of the message box if you are taking and testing the above-written code.
- You can use lesser techniques or functions to create a class for testing. Then, it can be modified later when we want to add more product functions and techniques.
- Consider similar or same features when we finish class and assign it to a module. Then, it will help us in comparing the features of different products.
Recommended Articles
This article is a guide to VBA Class. We discuss creating custom VBA classes and objects, examples, and a downloadable Excel template here. Below are some useful articles related to Excel VBA: –
- VBA Resize
- Use DateSerial in VBA
- Named Range in VBA
- ME Keyword in Excel VBA
- VBA VARTYPE Function
In this tutorial, we will provide an introduction to VBA class modules. You can create your own custom objects by using VBA class modules.
This allows you to take advantage of concepts, associated with object-oriented programming in your VBA code.
If you would like to learn more about VBA, then we have a whole course dedicated to Excel VBA.
Did you know that an Expert Excel user is 400% more likely to have recieved formal training compared to the average user? For more Excel facts read here.
How Is A Class Module Different To A Normal Module?
VBA is an object-oriented programming language. When you write VBA code, you will often be manipulating objects in some way.
Excel has standard built-in objects that are available, for you to work with. These include the application object, the workbook object, and the worksheet object among others.
Each of these objects have associated properties and methods.
To go over these topics in greater detail, please visit some of our previous VBA tutorials.
- Our Introduction to Macros article will get you up to speed with how to add the Developer Tab to the Ribbon. You will learn all about how to create simple macros in Excel. In addition, you will learn how to assign shortcut keys to macros and how to assign macros to buttons.
- Our Introduction to VBA article covers the VBA object model in Excel, and goes over what objects, properties and methods are. You will also learn about how to access the Visual Basic Editor. You will discover how to change the properties of objects.
- In our Mastering VBA Special Cells In Excel tutorial, we go over the Special Cells method in VBA, as well as the Activate, Select and Copy methods. This post will help you to get comfortable with using methods in your code. We also introduce you to Error Handling.
- The How to Use the COUNTA Function in Your VBA Code tutorial, covers the WorksheetFunction object. You will learn how to use the worksheet COUNTA Function and other built-in Excel worksheet functions in your VBA code.
When you insert a class module, you are creating the specifications for a custom object. VBA class modules are useful when you want to model real-world objects.
For example, let’s say a VBA developer working for a small plant nursery, wanted to create a plant object that was based on a plant class.
A plant has a common name and a scientific name. It’s life cycle can be classified as annual, perennial or biennial. It can be drought-resistant, of medium water requirements or needing to be watered constantly.
These are all the attributes that a plant has. This would be analogous to the properties of the plant object.
If the plant receives the right nutrients and conditions are optimal for growth, then the plant will grow. So, growth is analogous to a method. If the plant is a flowering plant, then the flowers will bloom at a certain time. This could be another method.
The nursery requires that each pot containing a plant be labelled, with the scientific name of the plant, its life cycle classification as well as it’s water needs. This labelling can be considered to be an action or method, associated with each plant.
A normal module on the other hand, is a place where you will store your sub procedures and functions. A normal module cannot be instantiated as a class.
Insert The Class Module
We are going to model the plant object that we described above, using a class module.
So, let’s look at how to insert a class module.
Step 1:
Press ALT-F11 on your keyboard in order to access the Visual Basic Editor.
Step 2:
Go to the Insert Tab and select Class Module.
You should see the following.
Creating the Template for An Object Item
The first thing we are going to do, is choose a name for our Class. When naming the class don’t give it the name of an existing built-in object or keyword.
Step 1:
So, in our case we’ll name our class clPlant.
-
- Note: In order to view all the built-in objects at your disposal. Go to the View Tab and select Object Browser.
Now you will be able to see all the built-in objects and their associated properties and methods.
You will also be able to see the new class that we just created.
You can close the window.
Step 2:
Enter the following code in the class module we just created.
Option Explicit
‘The properties
Public commonName As String
Public scientificName As String
Public classificationlifeSpan As String
Public wateringRequirements As String
‘This is the growth method
Public Sub plantGrowth()
‘We start by declaring two variables
Dim growthStatus As String
Dim growthstatusShape As ShapegrowthStatus = “is growing”
‘This line is telling Excel to read out the common name of the plant and state that the plant is growing
Application.Speech.Speak “The” & ” ” & commonName & ” ” & growthStatus
‘The message box displays the text giving the common name of the plant and saying that the plant is growing
MsgBox “The” & ” ” & commonName & ” ” & growthStatus, vbOKOnly, “This is the Growth Status of the Plant Currently”
‘We are creating a rectangle on the active sheet and specifying the position and size with this line
Set growthstatusShape = ActiveSheet.Shapes.AddShape(msoShapeRectangle, 70, 16, 170, 20)
‘We are filling the shape with a dark green colour
growthstatusShape.Fill.ForeColor.RGB = RGB(18, 90, 20)
‘We are stating the text that will be added to the shape
growthstatusShape.TextFrame.Characters.Text = “The plant is growing”
‘We are centre aligning the text in the shape
growthstatusShape.TextFrame2.TextRange.ParagraphFormat.Alignment = msoAlignCenterEnd Sub
‘This is the flower blooming method
Public Sub flowersBlooming()
‘We are declaring a variable
Dim flowerbloomingStatus As StringflowerbloomingStatus = “The flowers are blooming”
‘We are telling Excel to read out the words, the flowers are blooming
Application.Speech.Speak flowerbloomingStatus
‘The message box displays the text saying that the flowers are blooming
MsgBox flowerbloomingStatus, vbOKOnly, “This is the Flower Blooming Monitoring of the Plant Currently”
End Sub
‘This is the labelling method which is named generalInformation
Public Sub generalInformation()
‘We start by declaring two variables
Dim generalInformationText As String
Dim generalInformationShape As ShapegeneralInformationText = scientificName & Chr(13) & classificationlifeSpan & Chr(13) & wateringRequirements
‘We are creating a rectangle on the active sheet and specifying the position and size with this line
Set generalInformationShape = ActiveSheet.Shapes.AddShape(msoShapeRectangle, 70, 52.5, 170, 50)
‘Using the With…End With Construct we are changing some of the properties of this rectangle
With generalInformationShape
‘We are stating that the text that should be added to the rectangle should be the scientific name of the plant
‘the classification of it’s lifespan and it’s specific watering requirements
.TextFrame.Characters.Text = generalInformationText
‘We are filling the shape with a dark green colour
.Fill.ForeColor.RGB = RGB(18, 90, 20)
‘We are specifying that the font for the text should be in italics
.TextFrame2.TextRange.Font.Italic = msoTrue
End With
End Sub
Now let’s look at the code in greater detail. We start off by making four variable declarations in our class module. These will be the properties of our object. We use the Public statement.
We then create the methods of our custom object by using sub routines.
In our growth method, we are utilising the speak method of the built-in Speech object. By using this method we are able to make Excel play back the text string that we have input as an argument.
Use The Object!
We now want to use the custom object in our code.
Step 1:
To do this, go to the Insert Tab and select Module.
Step 2:
In the module create a sub procedure called mainCode and start by using the object. Here, we use the Dim keyword in combination with the New keyword.
You will notice that as you type, Intellisense will detect the new class that we just created. Select clPlant from the menu and press Tab.
Option Explicit
Sub mainCode()
Dim plant As New clPlant
‘We are specifying that the value of the commonName property is Bellflower, for our object
plant.commonName = “Bellflower”
‘We are specifying that the value of the scientificName property is Campanula portenschlagiana, for our object
plant.scientificName = “Campanula portenschlagiana”
‘We are specifying that the value of the classificationlifeSpan property is Perennial, for our object
plant.classificationlifeSpan = “Perennial”
‘We are specifying that the value of the watering requirements property is Medium watering requirements, for our object
plant.wateringRequirements = “Medium watering requirements”
‘We are now accessing the plantGrowth method of the object
plant.plantGrowth
‘We are now accessing the flowersBlooming method of the object
plant.flowersBlooming
‘We are now accessing the generalInformation method of the object
plant.generalInformation
End Sub
Now if we wanted to create another object based on our class, called plant1 for example, we could do this. This object would have all the properties and be able to access all the methods specified, in the clPlant class.
plant1 could be used for the Magnolia plant for example.
In this way, the class is used as a template for multiple copies of the object. You can also see that most of the difficult code is hidden from view when one is using the object in a normal module.
So let’s say more than one programmer was working on the application, they could use the custom objects in any of their modules with ease.
Looking for more on Excel? Read our guide to the XLOOKUP Function here.
Step 3:
Now we want to assign the mainCode macro to a shape. So, when the user clicks on the shape, the code will run.
On a blank sheet in the workbook, go to the Insert Tab and in the Illustrations Group, select the Icons option.
Type plant in the Search box to see all the icons that are related to plants.
Select the filled leaf icon and click the Insert button.
With the icon selected, go to the Graphics Format Tab and in the Change Group, select the Convert to Shape option.
Now with the shape selected. Go the Shape Format Tab, and in the Shape Styles Group, select Shape Fill.
Select the Green, Accent 6, Darker 25% fill.
Now right-click the shape and select Assign Macro…
Using the Assign Macro Dialog Box, select the mainCode macro and then click Ok.
Step 4:
Now make sure the shape is not selected. Then go to the View Tab and in the Show Group, uncheck the Gridlines option.
If you hover over the shape, the cursor should change.
If you press the shape. You should hear the words the Bellflower is growing, followed by a message box displaying the text.
You should then hear the words the flowers are blooming spoken, followed by a message box displaying the text.
Excel will then create two rectangles on the worksheet. One with text stating that the plant is growing and another containing the scientific name, life span classification and watering requirements of the Bellflower plant.
Advantages and Disadvantages Of Using Class Modules
Let’s review some advantages and disadvantages of using class modules.
Learning Objectives
You now know how to:
- Use a Class Module to create a custom object
- Specify the Properties and Methods of this custom object
- Call methods
- Make use of the Speech.Speak method to make Excel read text out loud
- Insert an Icon, convert it to a shape and assign a macro to the shape
Additionally, you have an understanding of:
- What the difference between a Class Module and a Normal module is
Conclusion:
Class modules are useful, when you need to create your own custom objects.
If you would like to delve into advanced VBA topics such as creating Add-Ins, then you should consider learning more about class modules.
Looking for more Excel tips? Check out the Creating a Data Model in Excel article here!
Special thank you to Taryn Nefdt for collaborating on this article!