Картинка: Designed by vectorjuice / Freepik Кому будет полезна статья, по мнению автора:начинающим программистам на языке VBA  и тем, кто не работал ранее с оператором Type. Если вы используете этот оператор постоянно, можно сравнить свой вариант применения и вариант автора. Большинство пользователей VBA прекрасно знают такую штуку как Type, он же User Defined Type (UDT). Кто-то, как я, использует его на повседневной основе. Кто-то, возможно, о нем слышал, но не мог понять как его применить. Лично я помню, как не так давно смотрел на этот Type и пытался понять зачем он мне нужен, ведь он просто хранит в себе переменные, которые можно с тем же успехом объявить в функции/процедуре или на уровне модуля? В этой статье я хотел бы показать на примере как можно использовать Type. Мы разберем некоторые его особенности, и возможно кто-нибудь из читателей найдет для себя один из примеров крайне интересным (а может быть даже будет использовать в своих проектах). Поехали! Что же, для начала давайте обратимся к официальной документации: (вольный перевод автора)Оператор Type – используется на уровне модуля для объявления пользовательского типа данных, содержащего один или несколько элементов.Type можно использовать только на уровне модуля. После объявления пользовательского типа вы можете объявить переменную этого типа в любом месте в пределах области видимости. Для объявления переменной пользовательского типа используйте Dim, Private, Public, ReDim или Static... Номера и метки строк не допускаются внутри блоков Type...End Type. Итак, исходя из документации мы можем выделить два основных момента: Оператор Type используется только на уровне модуля. Это значит, что его нельзя объявлять в процедурах/функциях/методах/свойствах. Номера и метки строк не допускаются внутри блоков. Давайте протестируем оба утверждения: В первом случае получаем ошибку компиляции "Недопустимая внутренняя процедура", во втором так же ошибка компиляции "Оператор (заявление/утверждение) недопустим внутри блока Type". Не описано в официальной документации то, что объявленный в Class модуле Type может быть только Private, иначе мы снова получим ошибку компиляции, в этот раз "Нельзя объявлять публичный пользовательский тип в объектном модуле": Компилятор перестает ругаться только в случае Private Type в Class модуле, но здесь нужно помнить, что возвращать такой UDT можно только Private функцией, иначе: мы снова получим ошибку компиляции, теперь это "Private перечисления и пользовательские типы, не могут использоваться в качестве параметров или возвращаемых типов для Public процедур, членов данных или полей пользовательских типов".Кстати, как и обозначено в описании ошибки, в модуле класса нельзя создавать публичные поля или использовать параметры для публичных методов с приватным типом UDT. Ну оно и логично. Итак, если я не ошибаюсь, с ошибками мы разобрались. Перейдем к использованию. Давайте представим, что наша задача – почтовая рассылка по определенному скрипту. Во время выполнения макроса мы получаем информацию об email-адресе получателя, адресате копии письма и его теме, после чего все эти данные нам нужно передать в отдельную функцию, которая занимается созданием письма и его отправкой или сохранением в черновики. Для начала разберемся с обычным модулем. Использование UDT в Class модуле будет во второй части. Как можно решить эту задачу стандартными средствами? Что ж, первое что мы делаем – объявляем переменные, которые будут содержать адрес получателя и адрес адресата копии (простите за тавтологию), а так же тему письма, после чего присваиваем напрямую значения, чтобы не усложнять пример, и отправляем их как аргументы в функцию CreateLetter: Далее, пропишем функцию, которая создаст и отправит или сохранит письмо (это значение сделаем необязательным, по умолчанию установим в False): Итак, в целом все нормально. У нас есть данные, мы передаем их в функцию, функция их использует. Но это всего лишь два адреса и тема.А теперь представим, что нам нужно передавать еще текст тела письма и вложение.А еще в параметрах можно указать нужно ли удалять письмо после отправки (свойство DeleteAfterSubmit), или указать нужно ли отметить неотправленное письмо (черновик) как прочитанное (свойство UnRead).А еще, возможно нам потребуется создавать письмо из другой процедуры и тогда снова придется перечислять все переменные в объявлении и передавать их все в функцию.И многое, многое другое...Представьте на секунду насколько сильно разрастутся параметры функции.Плюс, копия в письме может быть не всегда, как и вложение. Тогда придется делать все параметры Optional? Или прописать ParamArray? Это все не наглядно и может вызвать ошибки, в случае не верной передачи параметров.Код становится менее читаемым и сумбурным, согласитесь. На таком небольшом примере все ок, ничего особо критичного. Но в реальном проекте это может стать большой проблемой. Гораздо более лаконичное решение, как вы уже поняли, использовать UDT. Для решения нам потребуется объявить Type на уровне модуля и поместить в него все наши переменные. Давайте назовем его TLetter: Далее, в процедуре Mailing создадим переменную Letter типа TLetter: Теперь, всем полям нашего типа присваиваем необходимые значения. Сделать это можно написав имя переменной Letters и далее через точку выбрать нужное поле: Ничего вам это не напоминает?😏 Если вы сказали "да это же как объект" – то вы совершенно правы. Взаимодействие с Type очень похоже на взаимодействие с объектами. Только мы объявляем его без ключевых слов New и Set, как в случае с объектами,  а так же не сможем поместить в него функции/процедуры. Я бы даже назвал этот блок, скорее, своего рода, структурой. Все что нам осталось сделать – заменить в процедуре CreateLetter три старых параметра на один новый и переписать присваивание параметров: Кстати, в блоке Ошибки я забыл упомянуть еще одну небольшую особенность – UDT в параметры можно передавать только ByRef. Так лучше, верно? Не совсем. Давайте уберем последний опциональный параметр Submit из функции и пропишем его в нашей структуре как поле: Вот теперь действительно лучше.Обратите внимание, мы не присваиваем полю Submit значение в процедуре Mailing. Не присвоенное значение по умолчанию останется False: Думаю не нужно объяснять, что расширять этот тип можно сколько угодно, при этом использовать все его поля нет необходимости. Вы можете оставлять их пустыми и уже в функции прописывать валидацию для пустых полей, если в этом есть потребность. Итак, мы научились складывать несколько связанных переменных в одну и использовать их в качестве аргумента функции.Но что если нам нужно очень много таких переменных и при этом они связаны между собой в своего рода блоки, а эти блоки можно связать в одну единую переменную?Давайте добавим новые вводные в задачу и рассмотрим на примере. Допустим в функцию CreateLetter нам нужно дополнительно передавать параметр UnRead, а так же тело письма. Для начала разделим все наши вводные на несколько блоков: Блок адресатов: получатель, копия. Блок письма: тема и тело. Блок параметров: отправлять или нет, помечать как прочитанное или нет. Итого получаем три блока по две переменных в каждом.Как это реализовать? Очень просто. Для начала, под каждый блок создаем свой UDT: После чего снова создаем UDT TLetter, а уже в нем объявляем три переменных с ранее созданными блоками: Да, так можно было. 😁 Дальше, что называется, следите за руками. В процедуре Mailing через уже знакомую переменную Letter присваиваем значения переменнным блока адресатов и блока письма: Немного корректируем функцию CreateLetter и добавляем новые параметры для создаваемого элемента письма (не функции): И все! Да, так просто. В реальных задачах меня такая гибкость очень сильно выручала, выручает и, уверен, еще будет выручать. В итоге, мы имеем очень удобный и гибкий инструмент для хранения некой связанной структуры данных.Так же, этот инструмент помогает нам защитить код от ошибок на моменте его написания, потому что передать непонятно что в функцию будет сильно сложнее, чем если бы мы использовали обычные типы.Код, благодаря такому подходу, становится, во-первых, более читаемым, и во-вторых, более гибким и расширяемым. С таким кодом гораздо приятнее работать.  А ведь это важные вещи, к которым мы все стремимся при написании кода. Это не все, что я хотел рассказать про Type. В следующей статье рассмотрим еще один пример использования UDT в модуле, а так же увидим как его применять в Class модуле. Спасибо, что прочитали до конца.А как вы используете Type? Пишите в комментариях!А также, подписывайтесь на мой телеграмм.  Музыкант