Vba excel свой тип данных

Создание пользовательских типов данных в VBA Excel. Оператор Type, его описание и параметры. Создание массива «одномерных массивов» с пользовательскими данными.

Определение пользовательских типов данных

Пользовательский тип данных в VBA Excel представляет из себя набор элементов, каждому из которых пользователем присваивается свой тип данных. Другими словами, пользовательский тип данных — это набор данных разного типа, который может быть присвоен одной переменной.

Если простую переменную (не массив) объявить с пользовательским типом данных, она будет представлять из себя «одномерный массив»* с элементами разных типов данных, определенных пользователем.

Если с пользовательским типом данных объявить переменную массива, она будет представлять из себя массив «одномерных массивов»* пользовательских данных.

* Выражение «одномерный массив» взято в кавычки, так как фактически это не массив, а набор пользовательских данных, но для практического применения это не имеет значения.

Синтаксис оператора Type

Type <strong>Name</strong>

<strong>Element</strong>_1 as <strong>Tip</strong>

<strong>Element</strong>_2 as <strong>Tip</strong>

<strong>Element</strong>_3 as <strong>Tip</strong>

<strong>Element</strong>_n as <strong>Tip</strong>

End Type

Пользовательский тип данных в VBA Excel может быть объявлен с ключевым словом Public или Private, которое явно укажет зону его видимости. Если ключевое слово опущено, конструкция с оператором Type по умолчанию объявляется с зоной видимости Public.

Параметры оператора Type

Параметр Описание
Name Имя пользовательского типа данных, по которому этот тип данных будет присваиваться переменным.
Element Наименование отдельного элемента пользовательского типа данных.
Tip Тип данных отдельного элемента (стандартный тип VBA).

Применение пользовательских типов данных

Применение пользовательских типов данных в VBA Excel рассмотрим на примере домиков для животных.

Объявление пользовательского типа данных

Объявление пользовательского типа данных (конструкция с оператором Type) размещается в самом начале модуля в разделе Declarations.

Пример 1

Type Domik

naimenovaniye As String

obyem_m3 As Single

material As String

kolichestvo As Long

End Type

В этом примере:

  • Domik — имя, по которому этот тип данных будет присваиваться переменным;
  • naimenovaniye — наименование домика для животных;
  • obyem_m3 — объем домика в куб. метрах;
  • material — материал, из которого сделан домик;
  • kolichestvo — количество домиков на складе.

Заполнение данными массива

Обычно в качестве контейнеров для пользовательских типов данных в VBA Excel используются массивы. В простую переменную можно уместить только один набор пользовательских данных, а в массив — сколько нужно. В следующем примере мы заполним трехэлементный массив тремя наборами пользовательских данных.

Если представить набор пользовательских данных как «одномерный массив», то таким образом мы создадим массив «одномерных массивов» с пользовательскими данными.

Пример 2

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

Sub Primer2()

‘Объявляем трехэлементный массив

‘с пользовательским типом данных

Dim a(1 To 3) As Domik

‘Заполняем первый элемент массива

a(1).naimenovaniye = «Скворечник»

a(1).obyem_m3 = 0.02

a(1).material = «сосна»

a(1).kolichestvo = 15

‘Заполняем второй элемент массива

a(2).naimenovaniye = «Собачья будка»

a(2).obyem_m3 = 0.8

a(2).material = «береза»

a(2).kolichestvo = 5

‘Заполняем третий элемент массива

a(3).naimenovaniye = «Клетка кролика»

a(3).obyem_m3 = 0.4

a(3).material = «металл»

a(3).kolichestvo = 6

End Sub

Обращение к пользовательским данным в массиве

Для обращения в коде VBA Excel к пользовательским данным в массиве используется та же конструкция, что и при записи: указывается элемент массива и через точку наименование элемента пользовательских данных.

Пример 3

‘Считываем информацию из массива

Dim b As Variant

b = a(2).naimenovaniye

MsgBox b

b = a(3).obyem_m3

MsgBox b

b = «Мы продаем следующие товары: « _

& a(1).naimenovaniye & «, « _

& a(2).naimenovaniye & » и « _

& a(3).naimenovaniye

MsgBox b

Для наглядной демонстрации вставьте строки кода Примера 3 перед строкой End Sub Примера 2.


Картинка: Designed by vectorjuice / Freepik

Кому будет полезна статья, по мнению автора:
начинающим программистам на языке VBA и тем, кто не работал ранее с оператором Type. Если вы используете этот оператор постоянно, можно сравнить свой вариант применения и вариант автора.

Большинство пользователей VBA прекрасно знают такую штуку как Type, он же User Defined Type (UDT). Кто-то, как я, использует его на повседневной основе. Кто-то, возможно, о нем слышал, но не мог понять как его применить.

Лично я помню, как не так давно смотрел на этот Type и пытался понять зачем он мне нужен, ведь он просто хранит в себе переменные, которые можно с тем же успехом объявить в функции/процедуре или на уровне модуля?

В этой статье я хотел бы показать на примере как можно использовать Type. Мы разберем некоторые его особенности, и возможно кто-нибудь из читателей найдет для себя один из примеров крайне интересным (а может быть даже будет использовать в своих проектах). Поехали!

Вычисляем ошибки, чтобы их не допускать

Что же, для начала давайте обратимся к официальной документации:

(вольный перевод автора)
Оператор Type – используется на уровне модуля для объявления пользовательского типа данных, содержащего один или несколько элементов.Type можно использовать только на уровне модуля. После объявления пользовательского типа вы можете объявить переменную этого типа в любом месте в пределах области видимости. Для объявления переменной пользовательского типа используйте Dim, Private, Public, ReDim или Static… Номера и метки строк не допускаются внутри блоков Type…End Type.

Итак, исходя из документации мы можем выделить два основных момента:

  1. Оператор Type используется только на уровне модуля. Это значит, что его нельзя объявлять в процедурах/функциях/методах/свойствах.

  2. Номера и метки строк не допускаются внутри блоков.

Давайте протестируем оба утверждения:

В первом случае получаем ошибку компиляции «Недопустимая внутренняя процедура»,

ошибка компиляции при объявлении Type внутри процедуры

ошибка компиляции при объявлении Type внутри процедуры

во втором так же ошибка компиляции «Оператор (заявление/утверждение) недопустим внутри блока Type».

ошибка компиляции при объявлении Type с номером/меткой строки внутри блока

ошибка компиляции при объявлении Type с номером/меткой строки внутри блока

Не описано в официальной документации то, что объявленный в Class модуле Type может быть только Private, иначе мы снова получим ошибку компиляции, в этот раз «Нельзя объявлять публичный пользовательский тип в объектном модуле»:

ошибка компиляции при объявлении Public Type в Class модуле

ошибка компиляции при объявлении Public Type в Class модуле

Компилятор перестает ругаться только в случае Private Type в Class модуле, но здесь нужно помнить, что возвращать такой UDT можно только Private функцией, иначе:

ошибка компиляции при возврате приватного типа

ошибка компиляции при возврате приватного типа

мы снова получим ошибку компиляции, теперь это «Private перечисления и пользовательские типы, не могут использоваться в качестве параметров или возвращаемых типов для Public процедур, членов данных или полей пользовательских типов».
Кстати, как и обозначено в описании ошибки, в модуле класса нельзя создавать публичные поля или использовать параметры для публичных методов с приватным типом UDT. Ну оно и логично.

Постановка задачи

Итак, если я не ошибаюсь, с ошибками мы разобрались. Перейдем к использованию.

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

Решаем без UDT

Для начала разберемся с обычным модулем. Использование UDT в Class модуле будет во второй части.

Как можно решить эту задачу стандартными средствами?

Что ж, первое что мы делаем – объявляем переменные, которые будут содержать адрес получателя и адрес адресата копии (простите за тавтологию), а так же тему письма, после чего присваиваем напрямую значения, чтобы не усложнять пример, и отправляем их как аргументы в функцию CreateLetter:

Sub Mailing()
    Dim AddressTo As String: AddressTo = "exampleTo@test.vba"
    Dim AddressCC As String: AddressCC = "exampleCC@test.vba"
    Dim Subject   As String: Subject = "Тема письма"

    CreateLetter AddressTo, AddressCC, Subject
End Sub

Далее, пропишем функцию, которая создаст и отправит или сохранит письмо (это значение сделаем необязательным, по умолчанию установим в False):

заметьте как много параметров уже сейчас есть в этой функции

заметьте как много параметров уже сейчас есть в этой функции
Sub CreateLetter(ByVal AddressTo As String, _
                 ByVal AddressCC As String, _
                 ByVal Subject As String, _
                 Optional ByVal Submit As Boolean = False)
    Dim Outlook As Object
    Set Outlook = CreateObject("Outlook.Application")

    With Outlook.CreateItem(olMailItem)
        .To = AddressTo
        .CC = AddressCC
        .Subject = Subject
        If Submit Then .Send
    End With
End Sub

Итак, в целом все нормально. У нас есть данные, мы передаем их в функцию, функция их использует.

Но это всего лишь два адреса и тема.
А теперь представим, что нам нужно передавать еще текст тела письма и вложение.
А еще в параметрах можно указать нужно ли удалять письмо после отправки (свойство DeleteAfterSubmit), или указать нужно ли отметить неотправленное письмо (черновик) как прочитанное (свойство UnRead).
А еще, возможно нам потребуется создавать письмо из другой процедуры и тогда снова придется перечислять все переменные в объявлении и передавать их все в функцию.
И многое, многое другое…
Представьте на секунду насколько сильно разрастутся параметры функции.
Плюс, копия в письме может быть не всегда, как и вложение. Тогда придется делать все параметры Optional? Или прописать ParamArray? Это все не наглядно и может вызвать ошибки, в случае не верной передачи параметров.
Код становится менее читаемым и сумбурным, согласитесь. На таком небольшом примере все ок, ничего особо критичного. Но в реальном проекте это может стать большой проблемой.

Гораздо более лаконичное решение, как вы уже поняли, использовать UDT.

Решаем с UDT

Для решения нам потребуется объявить Type на уровне модуля и поместить в него все наши переменные. Давайте назовем его TLetter:

Type TLetter
    AddressTo As String
    AddressCC As String
    Subject   As String
End Type

Далее, в процедуре Mailing создадим переменную Letter типа TLetter:

обратите внимание, что IDE уже предлагает нам автокомплит данного типа и это прекрасно!

обратите внимание, что IDE уже предлагает нам автокомплит данного типа и это прекрасно!
Sub Mailing()
    Dim Letter As TLetter
    Dim AddressTo As String: AddressTo = "exampleTo@test.vba"
    Dim AddressCC As String: AddressCC = "exampleCC@test.vba"
    Dim Subject   As String: Subject = "Тема письма"

    CreateLetter AddressTo, AddressCC, Subject
End Sub

Теперь, всем полям нашего типа присваиваем необходимые значения. Сделать это можно написав имя переменной Letters и далее через точку выбрать нужное поле:

и вновь автокомплит в действии

и вновь автокомплит в действии
Sub Mailing()
    Dim Letter As TLetter
    Letter.AddressTo = "exampleTo@test.vba"
    Letter.AddressCC = "exampleCC@test.vba"
    Letter.Subject = "Тема письма"

    CreateLetter Letter
End Sub

Ничего вам это не напоминает?😏

Если вы сказали «да это же как объект» – то вы совершенно правы. Взаимодействие с Type очень похоже на взаимодействие с объектами. Только мы объявляем его без ключевых слов New и Set, как в случае с объектами, а так же не сможем поместить в него функции/процедуры. Я бы даже назвал этот блок, скорее, своего рода, структурой.

Все что нам осталось сделать – заменить в процедуре CreateLetter три старых параметра на один новый и переписать присваивание параметров:

Sub CreateLetter(ByRef Letter As TLetter, _
                 Optional ByVal Submit As Boolean = False)
    Dim Outlook As Object
    Set Outlook = CreateObject("Outlook.Application")
  
    With Outlook.CreateItem(olMailItem)
        .To = Letter.AddressTo
        .CC = Letter.AddressCC
        .Subject = Letter.Subject
        If Submit Then .Send
    End With
End Sub

Кстати, в блоке Ошибки я забыл упомянуть еще одну небольшую особенность – UDT в параметры можно передавать только ByRef.

Так лучше, верно?

Не совсем. Давайте уберем последний опциональный параметр Submit из функции и пропишем его в нашей структуре как поле:

Option Explicit

Type TLetter
    AddressTo As String
    AddressCC As String
    Subject   As String
    Submit    As Boolean ' Переносим параметр в структуру.
End Type

Sub Mailing()
    Dim Letter As TLetter
    Letter.AddressTo = "exampleTo@test.vba"
    Letter.AddressCC = "exampleCC@test.vba"
    Letter.Subject = "Тема письма"
  
    CreateLetter Letter
End Sub

Sub CreateLetter(ByRef Letter As TLetter)
    Dim Outlook As Object
    Set Outlook = CreateObject("Outlook.Application")

    With Outlook.CreateItem(olMailItem)
        .To = Letter.AddressTo
        .CC = Letter.AddressCC
        .Subject = Letter.Subject
        If Letter.Submit Then .Send ' Передаем поле из структуры.
    End With
End Sub

Вот теперь действительно лучше.
Обратите внимание, мы не присваиваем полю Submit значение в процедуре Mailing. Не присвоенное значение по умолчанию останется False:

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

Расширяем возможности

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

Допустим в функцию CreateLetter нам нужно дополнительно передавать параметр UnRead, а так же тело письма.

Для начала разделим все наши вводные на несколько блоков:

  1. Блок адресатов: получатель, копия.

  2. Блок письма: тема и тело.

  3. Блок параметров: отправлять или нет, помечать как прочитанное или нет.

Итого получаем три блока по две переменных в каждом.
Как это реализовать? Очень просто.

Для начала, под каждый блок создаем свой UDT:

Option Explicit

' Блок адресатов
Type TRecipient
    To As String
    CC As String
End Type

' Блок письма
Type TMain
    Subject As String
    Body    As String
End Type

' Блок параметров
Type TParameter
    Submit As Boolean
    UnRead As Boolean
End Type

После чего снова создаем UDT TLetter, а уже в нем объявляем три переменных с ранее созданными блоками:

Type TLetter
    Recipient  As TRecipient
    Main       As TMain
    Parameter  As TParameter
End Type

Да, так можно было. 😁

Дальше, что называется, следите за руками.

В процедуре Mailing через уже знакомую переменную Letter присваиваем значения переменнным блока адресатов и блока письма:

параметр передаваемый в функцию CreateLetter остается неизменным, это важно

параметр передаваемый в функцию CreateLetter остается неизменным, это важно
Sub Mailing()
    Dim Letter As TLetter
    Letter.Recipient.To = "exampleTo@test.vba"
    Letter.Recipient.CC = "exampleCC@test.vba"
    Letter.Main.Subject = "Тема письма"
    Letter.Main.Body = "Тело письма"

    CreateLetter Letter
End Sub

Немного корректируем функцию CreateLetter и добавляем новые параметры для создаваемого элемента письма (не функции):

Sub CreateLetter(ByRef Letter As TLetter)
    Dim Outlook As Object
    Set Outlook = CreateObject("Outlook.Application")

    With Outlook.CreateItem(olMailItem)
        .To = Letter.Recipient.To
        .CC = Letter.Recipient.CC
        .Subject = Letter.Main.Subject
        .Body = Letter.Main.Body
        .UnRead = Letter.Parameter.UnRead
        If Letter.Parameter.Submit Then .Send
    End With
End Sub

И все! Да, так просто.

В реальных задачах меня такая гибкость очень сильно выручала, выручает и, уверен, еще будет выручать.

Что в итоге

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

А ведь это важные вещи, к которым мы все стремимся при написании кода.

Это не все, что я хотел рассказать про Type. В следующей статье рассмотрим еще один пример использования UDT в модуле, а так же увидим как его применять в Class модуле.

Спасибо, что прочитали до конца.
А как вы используете Type? Пишите в комментариях!
А также, подписывайтесь на мой
телеграмм.

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.

Умели раньше пользоваться оператором Type?


16.67%
Да, использую его так же как автор
2


16.67%
Да, но использую его иначе (опишите, пожалуйста, как именно в комментариях)
2


25%
Нет, но слышал о нем. Не вижу смысла применять
3


25%
Нет, но слышал о нем. Возможно теперь буду применять
3


0%
Нет, и не слышал. Не вижу смысла применять
0


16.67%
Нет, и не слышал. Возможно теперь буду применять
2

Проголосовали 12 пользователей.

Воздержались 3 пользователя.

Содержание

  1. Объявление переменных
  2. Оператор Public
  3. Оператор Private
  4. Оператор Static
  5. Оператор Option Explicit
  6. Объявление объектной переменной для автоматизации
  7. См. также
  8. Поддержка и обратная связь
  9. Сводка типов данных
  10. Набор встроенных типов данных
  11. Преобразование между типами данных
  12. Проверка типов данных
  13. Возвращаемые значения функции CStr
  14. См. также
  15. Поддержка и обратная связь

Объявление переменных

При объявлении переменных обычно используется оператор Dim. Оператор объявления может быть помещен внутрь процедуры для создания переменной на уровне процедуры. Или он может быть помещен в начале модуля в разделе объявлений, чтобы создать переменную на уровне модуля.

В примере ниже создается переменная и указывается тип данных «String».

Если данный оператор отображается в процедуре, переменная strName может использоваться только в этой процедуре. Если же оператор отображается в разделе объявлений модуля, переменная strName доступна для всех процедур данного модуля, но недоступна для процедур других модулей проекта.

Чтобы предоставить доступ к переменной всем процедурам проекта, перед ней нужно поставить оператор Public, как показано в примере ниже:

Дополнительные сведения об именовании переменных см. в статье Правила именования в Visual Basic.

Переменные могут быть объявлены одним из следующих типов данных: Boolean, Byte, Integer, Long, Currency, Single, Double, Date, String (для строк переменной длины), String * length (для строк фиксированной длины), Object или Variant. Если тип данных не указан, по умолчанию присваивается тип данных Variant. Вы также можете создать определяемый пользователем тип с помощью оператора Type.

Вы можете объявить несколько переменных в одном операторе. Чтобы указать тип данных, необходимо задать тип данных для каждой переменной.

В приведенном ниже операторе переменные intX , intY и intZ объявлены типом Integer.

В приведенном ниже операторе intX и intY объявлены как Variant и только intZ объявлен как тип Integer.

Нет необходимости указывать тип данных переменной в операторе объявления. Если вы не укажите тип данных, переменной будет присвоен тип Variant.

Сокращение для объявления переменных x и y типом Integer в приведенном выше операторе

Сокращение для типов: % -integer; & -long; @ -currency; # -double; ! – Single; $ – String

Оператор Public

Используйте оператор Public для объявления общих переменных на уровне модуля.

Общие переменные могут использоваться в любой процедуре проекта. Если общая переменная объявлена в стандартном модуле или модуле класса, она может использоваться в любых проектах, содержащих ссылку на проект, в котором объявлена данная общая переменная.

Оператор Private

Используйте оператор Private для объявления частных переменных на уровне модуля.

Частные переменные могут использоваться только процедурами одного модуля.

На уровне модуля оператор Dim является эквивалентным оператору Private. Вы можете использовать оператор Private, чтобы упростить чтение и интерпретацию кода.

Оператор Static

Если для объявления переменной в процедуре используется оператор Static вместо оператора Dim, объявленная переменная сохраняет свое значение между вызовами в этой процедуре.

Оператор Option Explicit

В Visual Basic можно неявно объявить переменную, просто используя оператор присвоения значения. Все неявно объявленные переменные относятся к типу Variant. Для переменных типа Variant требуется больший объем памяти, чем для большинства других переменных. Приложение будет работать эффективнее, если переменные будут явно объявленными и им будет присвоен определенный тип данных. Явное объявление переменных снижает вероятность возникновения ошибок, вызванных конфликтом имен или опечатками.

Если вы не хотите, чтобы в Visual Basic были неявные объявления, то оператор Option Explicit должен стоять в модуле перед всеми процедурами. Этот оператор требует явного объявления всех переменных модуля. Если модуль содержит оператор Option Explicit, то при обнаружении необъявленной ранее переменной или опечатки в ее имени Visual Basic выдаст ошибку времени компиляции.

В программной среде Visual Basic имеется возможность задавать параметр, который будет автоматически включать оператор Option Explicit во все новые модули. Справочная информация по изменению параметров среды Visual Basic предоставлена в документации приложения. Обратите внимание, что данный параметр не меняет уже написанный код.

Статические и динамические массивы нужно объявлять в явном виде.

Объявление объектной переменной для автоматизации

При использовании приложения для управления объектами другого приложения необходимо указать ссылку на библиотеку типов этого другого приложения. Когда ссылка указана, можно объявлять объектные переменные в соответствии с наиболее подходящими для них типами. Например, если вы указываете ссылку на библиотеку типов Microsoft Excel при работе в Microsoft Word, то можете объявить переменную типа Worksheet внутри Word, чтобы она представляла объект Worksheet приложения Excel.

При использовании другого приложения для управления объектами Microsoft Access, как правило, можно объявлять объектные переменные согласно наиболее подходящим для них типам. Вы можете также использовать ключевое слово New для автоматического создания нового экземпляра объекта. Однако может возникнуть необходимость указать, что объект принадлежит Microsoft Access. Например, при объявлении объектной переменной, представляющей форму Access внутри Visual Basic, необходимо сделать различимыми объект Form приложения Access и объект Form приложения Visual Basic. Для этого следует включать имя библиотеки типов в объявление переменной, как показано в примере ниже:

Некоторые приложения не распознают отдельные объектные типы Access. Даже если в этих приложениях указана ссылка на библиотеку типов Access, все объектные переменные Access необходимо объявлять с типом Object. Также невозможно использовать ключевое слово New для создания нового экземпляра объекта.

В примере ниже показано, как объявлять переменную, представляющую экземпляр объекта Application Access в приложении, которое не распознает объектные типы Access. Затем приложение создает экземпляр объекта Application.

В документации приложения предоставлена информация о поддерживаемом им синтаксисе.

См. также

Поддержка и обратная связь

Есть вопросы или отзывы, касающиеся Office VBA или этой статьи? Руководство по другим способам получения поддержки и отправки отзывов см. в статье Поддержка Office VBA и обратная связь.

Источник

Сводка типов данных

Тип данных — это характеристика переменной, определяющая тип содержащихся в ней данных. К типам данных относятся типы, указанные в таблице ниже, а также пользовательские типы и определенные типы объектов.

Набор встроенных типов данных

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

Тип данных Размер хранилища Диапазон
Boolean 2 байта True или False
Byte 1 байт от 0 до 255
Collection Неизвестно Неизвестно
Currency (масштабируемое целое число) 8 байт от –922 337 203 685 477,5808 до 922 337 203 685 477,5807
Date 8 байт от 1 января 100 г. до 31 декабря 9999 г.
Decimal 14 байт +/–79 228 162 514 264 337 593 543 950 335 без десятичной запятой

+/–7,9228162514264337593543950335 с 28 разрядами справа от десятичной запятой

Наименьшее ненулевое число равно +/–0,0000000000000000000000000001

Dictionary Неизвестно Неизвестно
Double (число с плавающей запятой двойной точности) 8 байт от –1,79769313486231E308 до –4,94065645841247E-324 для отрицательных значений

от 4,94065645841247E-324 до 1,79769313486232E308 для положительных значений

Integer 2 байта от –32 768 до 32 767
Long (целое число Long) 4 байта от –2 147 483 648 до 2 147 483 647
LongLong (целое число LongLong) 8 байт от –9 223 372 036 854 775 808 до 9 223 372 036 854 775 807

Действительно только для 64-разрядных платформ.

LongPtr (целое число Long в 32-разрядных системах, целое число LongLong в 64-разрядных системах) 4 байта в 32-разрядных системах

8 байт в 64-разрядных системах

от –2 147 483 648 до 2 147 483 647 в 32-разрядных системах

от –9 223 372 036 854 775 808 до 9 223 372 036 854 775 807 в 64-разрядных системах

Object 4 байта Любая ссылка на Object
Single (число с плавающей запятой одинарной точности) 4 байта от –3,402823E38 до –1,401298E-45 для отрицательных значений

от 1,401298E-45 до 3,402823E38 для положительных значений

String (переменная длина) 10 байтов + длина строки от 0 до приблизительно 2 миллиардов
String (фиксированная длина) Длина строки от 1 до приблизительно 65 400
Variant (с числами) 16 байт Любое числовое значение до диапазона типа Double
Variant (с символами) 22 байта + длина строки (24 байтов в 64-разрядных системах) Тот же диапазон как для типа String переменной длины
Определяется пользователем (используя Type) Число, необходимое для элементов Диапазон каждого элемента совпадает с диапазоном его типа данных.

Тип Variant, содержащий массив, требует на 12 байт больше, чем сам массив.

Для массивов данных любого типа требуются 20 байтов памяти плюс 4 байта на каждую размерность массива, плюс количество байтов, занимаемых самими данными. Память, занимаемая данными, может быть вычислена путем умножения количества элементов данных на размер каждого элемента.

Например, данные в одномерном массиве, состоящем из 4 элементов данных Integer размером 2 байта каждый занимают 8 байтов. 8 байтов, необходимых для данных, плюс 24 байта служебных данных составляют 32 байта полной памяти, требуемой для массива. На 64-разрядных платформах массив SAFEARRAY занимает 24 бита (плюс 4 байта на оператор Dim). Элемент pvData является 8-байтным указателем, и он должен находиться в границах 8 байтов.

Тип LongPtr не является настоящим типом данных, так как он преобразуется в тип Long в 32-разрядных средах или в тип LongLong в 64-разрядных средах. Тип LongPtr должен использоваться для представления указателя и обработки значений в операторах Declare и позволяет писать переносимый код, который может выполняться как в 32-разрядных, так и в 64-разрядных средах.

Для преобразования одного типа строковых данных в другой используется функция StrConv.

Преобразование между типами данных

В статье Функции преобразования типов приведены примеры использования следующих функций для приведения выражения к определенному типу данных: CBool, CByte, CCur, CDate, CDbl, CDec, CInt, CLng, CLngLng, CLngPtr, CSng, CStr и CVar.

Ниже приведены страницы соответствующих функций: CVErr, Fix и Int.

Функция CLngLng действительна только для 64-разрядных платформ.

Проверка типов данных

Чтобы проверить типы данных, ознакомьтесь с приведенными ниже функциями.

Возвращаемые значения функции CStr

Если expression CStr возвращает
Boolean Строка, содержащая значение True или False.
Date Строка, содержащая полный или краткий формат даты, установленный в системе.
Empty Строка нулевой длины («»).
Error Строка, содержащая слово Error и номер ошибки.
Null Ошибка во время выполнения.
Другое числовое значение Строка, содержащая число

См. также

Поддержка и обратная связь

Есть вопросы или отзывы, касающиеся Office VBA или этой статьи? Руководство по другим способам получения поддержки и отправки отзывов см. в статье Поддержка Office VBA и обратная связь.

Источник

VBA Type

When thinking about custom objects / types in VBA most users turn to the VBA Class and create additional modules that quickly start cluttering their VBA Projects. Often these turn out to be simply structures, for the sole purpose of grouping several variables within a single object, bereft of any procedures or functions. VBA does not allow you to create subfolders in your VBA Project, so next time consider creating a VBA Type instead!

Below an example VBA Type name Person as well as usage:

'Declaration of Person Type
Type Person
  name as String
  surname as String
  age as Long
End Type

'Example usage of created Type
Sub TestType
  Dim p as Person
  p.name = "Tom"
  p.surname = "Hanks"
  p.age = 54

  Debug.Print p.name & " " & p.surname & ", age " & p.age
  'Result: Tom Hanks, age 54"
End Sub

What is a VBA Type and why use it?

What is a VBA Type? The Type statement defines a new user-defined data type containing one or more variables (basic VBA data types, custom Types or objects including custom VBA Classes). VBA Types fill the gap between native basic VBA data types such as Strings, Integers, Booleans etc. and custom objects – VBA Classes. Type declarations can only consist of variables.

Why would you want to use a VBA Type? Why use a cannon to shoot a fly? i.e why create a new VBA Class when all you need to is to group several variables into a new data type. One other thing is that Types don’t need separate VBA Modules and are embedded into existing modules, saving you space and preventing VBA Module cluttering.

Syntax of the VBA Type

The VBA Typed can be defined within VBA Modules, at the beginning, similarly like global variables. The syntax of a VBA Type looks like:

Type TypeName
  VariableName [as VariableType]
  '...
End Type

VBA Types can:

  • Have object such as other VBA Types or VBA Classes as variables
  • Contain arrays – both static (predefined size) and dynamic (dynamically sized/resized)
  • Be declared within VBA Modules. Types can also be declared within Forms and Classes, however, have to be declared Private (scoped only to Form or Class)

VBA Types can’t:

  • Contain procedures or functions
  • Be declared as Public in Classes and Forms

VBA Types are treated like other types (not objects) hence does not require to be defined using the Set statement. A Type object can be immediately used after it has been declared.

Type SomeType
   num as Long
   '...
End Type

Sub TestType()
  Dim myType as SomeType
  Set myType = New SomeType 'WRONG!
  myType.num = 10 'OK!
End Sub

VBA Type examples

Now that we now the basic let’s look at some VBA Type examples. The Type example below is an excellent example of how much you can “squeeze” into a custom VBA Types.

Type CustomType
    someString As String
    threeCharacterStrings As String * 3
    someNumber As Long
    someEnum As CustomEnum 
    someObject As Object
    predefinedSizeArray(10) As Long
    dynamicSizedArray() As Long
End Type

Now let’s use this newly defined VBA Type:

Enum CustomEnum
    EnumValue1
    EnumValue2
    EnumValue3
End Enum

Sub TestCustomType()
    Dim myType As CustomType
    myType.someString = "Hello there!"
    myType.threeCharacterStrings = "Hello!" 'Will be trimmed to "Hel"
    myType.someEnum = EnumValue1
    myType.someNumber = 10
    Set myType.someObject = CreateObject("Scripting.Dictionary")
    myType.predefinedSizeArray(0) = 20
    ReDim myType.dynamicSizedArray(20) As Long
    myType.dynamicSizedArray(1) = 100
End Sub

The VBA Type can basically aggregate any VBA Type within – including objects, classes, enums etc.

Types are typically compared to VBA Classes. Similarly most VBA users start by creating a class instead of contemplating the use of a simple VBA Type. Let’s thus compare the 2.

Property VBA Type VBA Class
Contains variables
  • Types can contain only Public variables
  • Classes can contain both Public and Private variables
Contains procedures / functions
  • Types cannot contain procedures, functions or properties
  • Classes can contain procedures, functions and properties
Need to be defined using the Set statement
  • Types don’t require the Set statement, thus allocate memory when they are declared
  • Classes need to defined to be used, this don’t allocate memory until they are created and can be unallocated by being Set to Nothing
Can be declared in any module
  • True. Can be declared as Public/Private in VBA Modules and Private in Classes and Forms
  • Classes only be declared in dedicated Class modules

VBA Type vs Module

Consider the reasons for creating new objects. In general I encourage the creation of Types and Classes as this goes in line with Object-Oriented Programming, however sometimes this is an overkill.
vba moduleAre you creating the new Type / Class to have an easily accessible list of variables? Why not use a VBA Module instead. Simply make sure you variables are Public.

VBA Modules are singletons – objects accessible within the whole project and which variables have a single instance. As you probably know Modules like Classes can have procedures and functions.

VBA Types on the other hand are a simple vehicle of grouping variables into a new data type. Hence, don’t use them as singletons.

Summary

Hopefully, now you will consider using VBA Types when pondering a reason for creating a new custom VBA structure. I encourage you to use VBA Types and VBA Classes more often as you will notice how your code becomes more readable, comprehensive and less redundant. Do read my post on writing shorter VBA code if you want to read more tips on VBA best practices.

In my last week’s Blog post I elaborated on data types and their use in VBA.

Beyond using the readily available native data types we’re all familiar with, such as Integer, Date, String and Boolean, we can define our own, user-defined data type.

What is a User-Defined Data Type?

Simply put, a user-defined data type is a group of variables of native data types.

Here’s an example of grouping together several variables to define a new data type representing (or holding) information about a vehicle:

Type Vehicle

   VIN as Long

   Make as String

   Model as String

   Year as Integer

End Type

That’s it! As simple as that.

We can now dimension variables of type Vehicle:

Dim vehLeasedCar as Vehicle

We now have a kind of a hierarchy: a variable of type Vehicle containing sub-variables of different (native) types: Long, String and Integer in this example.

We can think of the sub-variables as properties of the Vehicle-type variable and use the “dot” to address each of these sub-variables. Let’s assign some values to our vehLeasedCar variable:

vehLeasedCar.VIN = 4656418

vehLeasedCar.Make = “Ford”

vehLeasedCar.Model = “Taurus”

vehLeasedCar.Year = 2001

In the same way, we can read the values of our variable (or its sub-variables). Let’s print out our vehicle’s model:

Debug.Print vehLeasedCar.Model

Why use User-Defined Data Types?

I can think of two main reasons to make use of user-defined data types:

  1. They are suitable for records arrangement in a readable format
  2. They are very efficient to process, even more than collections

Think of a process that needs to store and manipulate 5,000 vehicles.

One way would be to have a two-dimensional array store a table-like structure of the vehicles. This is valid (and you know how much I love arrays), and even performant. However, it would not be that clear by viewing the code what we are doing. What do you find more telling in your code: arrCars(i,j) or arrCars(i).Model?

Let’s define another data type to store information about an employee:

Private Type TEmployeeRecord

    Name As String

    DOB As Date

    Age As Integer

    City As String

    Score As Integer

End Type

Consider how readable and self-explanatory the following Sub is, tasked with printing out all data of an employee, passed over as a TEmployeeRecord type variable:

Sub PrintEmployeeReport(employee As TEmployeeRecord)

    With employee

        Debug.Print .Name, .City, .DOB, .Age, .Score

    End With

End Sub

Storing Many Records in an Array

Of course, storing a single record, as in the above examples, is not very helpful. We typically need to store many records of data in our program.

For this, we can combine our own defined data type (record structure) with a one-dimensional array.

To illustrate this in an example, let’s pick up a list of employees with some data on them from an Excel table and arrange it in an array of employees’ records. Our table look like this:

Excel VBA User Defined Data Types

We first use a 2-dimensional array as an interim structure to quickly read the table from the Worksheet:

Dim arrEmployees() As Variant

arrEmployees = ThisWorkbook.Worksheets("Scores").Range("ScoresTable[#Data]").Value

Next, we loop our array and transfer each employee (“row”) in the array into a new, 1-dimentional array, holding employees’ records:

Dim employees(5) As TEmployeeRecord

Dim i As Integer

For i = LBound(arrEmployees, 1) To UBound(arrEmployees, 1)

        With employees(i - 1)

            .Name = arrEmployees(i, 1)

            .DOB = arrEmployees(i, 2)

            .Age = arrEmployees(i, 3)

            .City = arrEmployees(i, 4)

            .Score = arrEmployees(i, 5)

        End With

Next i

With that, we have an efficient array of employees to work with.

We can now write a function to return the score of an employee, given a pointer to the array of employees we prepared and the employee name to look for.

Function GetScoreOfEmployee(strName As String, employees() As TEmployeeRecord) As Integer

    Dim i As Integer  

    GetScoreOfEmployee = 0 

    For i = LBound(employees) To UBound(employees)

        With employees(i)

            If (.Name = strName) Then

                GetScoreOfEmployee = .Score

                Exit For

            End If

        End With

    Next i

End Function

See how elegant and clear this function is? We’re looping the employees array, checking each employee name to match strName we’re looking for, returning his score upon a successful match.

Here’s how we can make use of the above function to ask the user for a name of an employee and get his score (we’ll store his score in the intScore variable). We’re making use of the employees array we have populated earlier with our employees:

Dim strName As String

Dim intScore As Integer

strName = InputBox("Name of employee:", "Query Employee Form")

intScore = GetScoreOfEmployee(strName, employees)

If (intScore = 0) Then

   MsgBox "Employee not found", vbOKOnly + vbInformation, "Employee Error"

Else

   MsgBox strName & "'s score: " & intScore, vbOKOnly + vbInformation, "Employee Score Result"

End If

By the way, if you are not familiar with the InputBox function, I have a detailed Blog post about it for you here. Similarly, the MsgBox function is explained here.

Unfortunately, Excel VBA doesn’t allow us to add user-defined type variables to a collection, therefore we’re missing out on a potentially very useful and efficient data structure. One can argue that if a collection is our best structure to maintain our records, we can implement our records as objects defined in a Class Module instead of a user-defined data type. Yes, I have a series of Blog posts about objects and Class Modules starting right here.

The above examples are featured in my flagship on-line course: Computer Programming with Excel VBA, in case they seemed familiar to you 😉.

Hey, a small request from me to you: please share this Blog post so that we can help more colleagues with Excel VBA.

Понравилась статья? Поделить с друзьями:
  • Vba excel работа с числами
  • Vba excel свернуть группировку
  • Vba excel работа с формулами
  • Vba excel самоучитель книга
  • Vba excel работа с формами