Vba конструктор класса с параметрами

Обновлено: 27.04.2024

Visual Basic обеспечивает полную поддержку объектно-ориентированного программирования, включая инкапсуляцию, наследование и полиморфизм.

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

Наследование описывает возможность создания новых классов на основе существующих классов.

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

В этом разделе рассматриваются следующие понятия.

Классы и объекты

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

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

Дополнительные сведения см. в разделе:

Члены класса

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

Свойства и поля

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

Для работы со свойствами используются процедуры "Get" и "Set", которые расширяют возможности управления способом задания и возврата значений.

Visual Basic позволяет создать частное поле для хранения значения свойства или использовать так называемые автоматически реализуемые свойства, которые автоматически создают это поле и предоставляют базовую логику для процедур свойств.

Определение автоматически реализуемого свойства:

Если требуется выполнить дополнительные операции чтения и записи применительно к значению свойства, определите поле для хранения значения свойства и затем реализуйте базовую логику для хранения и извлечения этого значения:

У большинства свойств есть методы или процедуры для задания и возврата значения свойства. Однако можно создать свойства, доступные только для чтения или только на запись, чтобы запретить изменение или чтение значений свойств. Для этого в Visual Basic можно использовать ключевые слова ReadOnly и WriteOnly . Следует отметить, что автоматически реализуемые свойства нельзя сделать доступными только для чтения или только для записи.

Дополнительные сведения см. в разделе:

Методы

Действие, которое выполняет объект, называется методом.

В Visual Basic существует два способа создания метода: с помощью оператора Sub (если метод не возвращает значение) или с помощью оператора Function (если метод возвращает значение).

Определение метода класса:

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

Как правило, метод объявляется при определении класса. Однако Visual Basic также поддерживает методы расширения, позволяющие добавлять методы в существующий класс вне фактического определения класса.

Дополнительные сведения см. в разделе:

Конструкторы

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

Определение конструктора для класса:

Деструкторы

События

События позволяют классу или объекту уведомлять другие классы или объекты о возникновении каких-либо ситуаций. Класс, отправляющий (или порождающий) событие, называется издателем, а классы, принимающие (или обрабатывающие) событие, называются подписчиками. Дополнительные сведения о том, как порождаются и обрабатываются события, см. в разделе События.

Чтобы объявить события, используйте оператор Event.

Чтобы вызвать события, используйте оператор RaiseEvent.

Чтобы указать обработчики событий с помощью декларативного способа, используйте инструкцию WithEvents и предложение Handles .

Чтобы динамически добавлять, удалять и изменять обработчик событий, связанный с событием, используйте оператор AddHandler и RemoveHandler вместе с оператором AddressOf.

Вложенные классы

Класс, определенный внутри другого класса, называется вложенным. По умолчанию вложенный класс является частным.

Чтобы создать экземпляр вложенного класса, укажите имя класса контейнера и имя вложенного класса, используя в качестве разделителя точку:

Модификаторы доступа и уровни доступа

С помощью модификаторов доступа все классы и члены классов могут указывать уровни доступа, предоставляемые другим классам.

Имеющиеся модификаторы доступа указаны в следующей таблице.

Модификатор Visual Basic Определение
Public Доступ к типу или члену возможен из любого другого кода в той же сборке или другой сборке, ссылающейся на него.
Частная Доступ к типу или члену можно получить только из кода в том же классе.
Защищены Доступ к типу или члену можно получить только из кода в том же классе или в производном классе.
Друг Доступ к типу или члену возможен из любого кода в той же сборке, но не из другой сборки.
Protected Friend Доступ к типу или члену возможен из любого кода в той же сборке, или из производного класса в другой сборке.

Создание экземпляров классов

Чтобы создать объект, необходимо создать экземпляр класса.

После создания экземпляра класса можно присваивать значения свойствам и полям экземпляра и вызывать методы класса.

Чтобы назначить значения свойствам в процессе создания экземпляра класса, используйте инициализаторы объектов:

Дополнительные сведения можно найти в разделе

Общие классы и члены

Общий член класса — это свойство, процедура или поле, совместно используемое всеми экземплярами класса.

Чтобы определить общий член, выполните следующие действия.

Чтобы получить доступ к общему члену, используйте имя класса без создания объекта этого класса:

Общие модули в Visual Basic имеют только общие члены и не могут быть созданы. Общие члены также не могут получить доступ к не общим свойствам, полям или методам

Дополнительные сведения см. в разделе:

Анонимные типы

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

Создание экземпляра анонимного типа:

Дополнительные сведения можно найти в разделе Анонимные типы.

Наследование

Visual Basic не поддерживает множественное наследование. То есть можно указать только один базовый класс для производного класса.

Наследование от базового класса:

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

Указание, что класс не может использоваться в качестве базового класса:

Указание, что класс может использоваться только в качестве базового класса и нельзя создать экземпляр этого класса:

Дополнительные сведения можно найти в разделе

Переопределение элементов

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

Следующие модификаторы используются для управления переопределением свойств и методов.

Модификатор Visual Basic Определение
Overridable Разрешает переопределение члена класса в производном классе.
Переопределения Переопределяет виртуальный (переопределяемый) член в базовом классе.
NotOverridable Запрещает переопределение члена в наследующем классе.
MustOverride Требует, чтобы член класса был переопределен в производном классе.
Тени Скрывает член, наследуемый от базового класса

Интерфейсы

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

Реализация интерфейса в классе:

Дополнительные сведения см. в разделе:

Универсальные шаблоны

Определение универсального класса:

Создание экземпляра универсального класса:

Дополнительные сведения можно найти в разделе

Делегаты

Делегат — это тип, который определяет сигнатуру метода и может обеспечивать связь с любым методом с совместимой сигнатурой. Метод можно запустить (или вызвать) с помощью делегата. Делегаты используются для передачи методов в качестве аргументов к другим методам.

Обработчики событий — это ничто иное, как методы, вызываемые с помощью делегатов. Дополнительные сведения об использовании делегатов при обработке событий см. в разделе События.

Создание ссылки на метод, сигнатура которого соответствует сигнатуре, указанной делегатом:

How can you construct objects passing arguments directly to your own classes?

Something like this:

Not being able to do this is very annoying, and you end up with dirty solutions to work this around.

For Immutability a Private Init and a Factory inside the class can be used: Private VBA Class Initializer called from Factory

Follow-up on the above comment. The Private Class initializer is now supported on GitHub within this repo. Method is called RedirectInstance and needs to be called from a Private Function. In conjunction with a Class Factory the immutability is achieved.

7 Answers 7

Here's a little trick I'm using lately and brings good results. I would like to share with those who have to fight often with VBA.

1.- Implement a public initiation subroutine in each of your custom classes. I call it InitiateProperties throughout all my classes. This method has to accept the arguments you would like to send to the constructor.

2.- Create a module called factory, and create a public function with the word "Create" plus the same name as the class, and the same incoming arguments as the constructor needs. This function has to instantiate your class, and call the initiation subroutine explained in point (1), passing the received arguments. Finally returned the instantiated and initiated method.

Let's say we have the custom class Employee. As the previous example, is has to be instantiated with name and age.

This is the InitiateProperties method. m_name and m_age are our private properties to be set.

And now in the factory module:

And finally when you want to instantiate an employee

Especially useful when you have several classes. Just place a function for each in the module factory and instantiate just by calling factory.CreateClassA(arguments), factory.CreateClassB(other_arguments), etc.

As stenci pointed out, you can do the same thing with a terser syntax by avoiding to create a local variable in the constructor functions. For instance the CreateEmployee function could be written like this:

What's the benefit of a factory module over, say, a Construct method in each class. So you would call Set employee_obj = New Employee then employee_obj.Construct "Johnny", 89 and the construction stuff happens inside the class. Just curious.

Hi, I see some benefits. It can be that they overlap somewhat. First of all, you use a constructor in a standard way like you would in any normal OOP language, which enhances clarity. Then, each time you instantiate an object, you save that line to initiate your object which makes you write less, then you CANNOT forget to initialize the object, and finally there is one concept less on your procedure, which reduces complexity.

You can keep track of it with a private variable. You can define Class_Initialize , and then define there a variable m_initialized = false . When you enter the InitiateProperties , you check the m_initialized and if it is false, continue and at the end, set it to true. If it is true, raise an error or do nothing. If you call again the InitiateProperties method, it will be true and the state of the object won't be changed.

Sorry to wake this up from the dead, but that last paragraph is wrong, that is not "nicer" code. Treating a function's return mechanism (assign to identifier) as a declared local variable is misleading and confusing at best, not "nicer" (looks like a recursive call, doesn't it?). If you want terser syntax, leverage module attributes as illustrated in my answer. As a bonus you lose that Single-Responsibility-Principle-takes-a-beating "factory bag" module responsible for creating instances of just about everything and its mother and inevitably becomes a mess in any decent-size project.

I use one Factory module that contains one (or more) constructor per class which calls the Init member of each class.

For example a Point class:

And a Factory module:

One nice aspect of this approach is that makes it easy to use the factory functions inside expressions. For example it is possible to do something like:

It's clean: the factory does very little and it does it consistently across all objects, just the creation and one Init call on each creator.

And it's fairly object oriented: the Init functions are defined inside the objects.

EDIT

I forgot to add that this allows me to create static methods. For example I can do something like (after making the parameters optional):

Unfortunately a new instance of the object is created every time, so any static variable will be lost after the execution. The collection of lines and any other static variable used in this pseudo-static method must be defined in a module.

It's been a long time since I last played with VBA, but. 1: how do you get constructed objects from the Factory 's subroutines? the definition of Sub entails no return value. 2: even with the point I am missing, your Factory 's do pretty much the same thing as mine: create an object (I do it in two steps, your syntax is clearly shorter), call an Init / InitiateProperties method, and in my case, explicitly return.

@ikaros45 They were supposed to be Function , not Sub , I edited the post, thanks. Yes, it is the same as yours, it's just organized in a way easier to manage (in my opinion) as the number of classes and the number of "constructors" for each class grows.

Yeap well, the organization is exactly the same, but I agree that your way it's more succint. It means the same but you save two lines per constructor function, which is nice. If you don't mind, I'll update my code with your syntax.

When you export a class module and open the file in Notepad, you'll notice, near the top, a bunch of hidden attributes (the VBE doesn't display them, and doesn't expose functionality to tweak most of them either). One of them is VB_PredeclaredId :

Set it to True , save, and re-import the module into your VBA project.

Classes with a PredeclaredId have a "global instance" that you get for free - exactly like UserForm modules (export a user form, you'll see its predeclaredId attribute is set to true).

A lot of people just happily use the predeclared instance to store state. That's wrong - it's like storing instance state in a static class!

Instead, you leverage that default instance to implement your factory method:

With that, you can do this:

Employee.Create is working off the default instance, i.e. it's considered a member of the type, and invoked from the default instance only.

Problem is, this is also perfectly legal:

And that sucks, because now you have a confusing API. You could use '@Description annotations / VB_Description attributes to document usage, but without Rubberduck there's nothing in the editor that shows you that information at the call sites.

Besides, the Property Let members are accessible, so your Employee instance is mutable:

The trick is to make your class implement an interface that only exposes what needs to be exposed:

And now you make Employee implement IEmployee - the final class might look like this:

Notice the Create method now returns the interface, and the interface doesn't expose the Property Let members? Now calling code can look like this:

And since the client code is written against the interface, the only members empl exposes are the members defined by the IEmployee interface, which means it doesn't see the Create method, nor the Self getter, nor any of the Property Let mutators: so instead of working with the "concrete" Employee class, the rest of the code can work with the "abstract" IEmployee interface, and enjoy an immutable, polymorphic object.

Note: immutable isn't really achievable; the instance has access to its fields and can very well mutate their values. But it's better than exposing Property Let to the outside world (or worse, public fields!)

wouldn't the calling code be Dim empl as Employee since the Employee Class Implements IEmployee otherwise runtime error with the way you have it written

@Matheiu Guindon - don't mean to spam, but I am revisiting this post almost 3 months later. Since, I have been reading your rubberduck blog over and over on OOP, and this answer now makes complete sense to me. I can't believe the questions I asked in the comments above.

@Tom FWIW the VBIDE autocomplete is actually still stuck in 1997, but anyway if I wrote this answer/code today (it's 5 years old), I'd probably have used PascalCase identifier names everywhere. I agree empl is a clumsy prefix, but to your point about using the full type name as a suffix, I'd perhaps do this for the names of related classes or different implementations of an interface, but not for a variable or a property: empl would be Employee As IEmployee , and emplName would be Name . That said your comment is.. a bit hard to read.. but thanks! ;-)

Using the trick

I found another more compact way:

As you can see the New_ constructor is called to both create and set the private members of the class (like init) only problem is, if called on the non-static instance it will re-initialize the private member. but that can be avoided by setting a flag.

First, here is a very quick summary/comparison of the baseline approach and the top three answers.

The baseline approach: These are the basic ways to construct new instances of a class:

Above, Construct would be a sub in the Lunch class that assigns the parameter values to an object.

The issue is that even with a method, it took two lines, first to set the new object, and second to fill the parameters. It would be nice to do both in one line.

1) The Factory class (bgusach): Make a separate class ("Factory"), with methods to create instances of any other desired classes including set-up parameters.

When you type "f." in the code window, after you Dim f as Factory , you see a menu of what it can create via Intellisense.

2) The Factory module (stenci): Same, but instead of a class, Factory can be a standard module.

In other words, we just make a function outside the class to create new objects with parameters. This way, you don't have to create or refer to a Factory object. You als don't get the as-you-type intellisense from the general factory class.

3) The Global Instance (Mathieu Guindon): Here we return to using objects to create classes, but sticking to the class-to-be-made. If you modify the class module in an external text editor you can call class methods before creating an object.

Here MakeNew is a function like CreateEmployee or CreateLunch in the general factory class, except that here it is in the class-to-be-made, and so we don't have to specify what class it will make.

This third approach has a fascinating "created from itself" appearance to it, permitted by the global instance.

Other ideas: Auto-instancing, a Clone method, or a Parent Collection Class With auto instancing ( Dim NewEmployee as new Employee , note the word "new"), you can achieve something similar to the global instance without the setup process:

With "new" in the Dim statement, the NewEmployee object is created as an implied pre-step to calling its method. Construct is a Sub in the Employee class, just like in the baseline approach. [1]

There are some issues with auto instancing; some hate it, a few defend it. 2 To restrict auto-instancing to one proto-object, you could add a MakeNew function to the class as I used with the Global Instance approach, or revise it slightly as Clone :

Here, the Clone method could be set up with optional parameters Function Clone(optional employeeName, optional employeeAge) and use the properties of the calling object if none are supplied.

Even without auto-instancing, a MakeNew or Clone method within the class itself can create new objects in one line, once you create the proto-object. You could use auto-instancing for a general factory object in the same way, to save a line, or not.

Finally, you might want a parent class. A parent class could have methods to create new children with parameters (e.g., with Employees as a custom collection, set newEmployee = Employees.AddNew(Tom, 38) ). For a lot of objects in Excel this is standard: you can't create a Worksheet or a Workbook except from its parent collection.

[1] One other adjustment relates to whether the Construct method is a Sub or a Function. If Construct is called from an object to fill in its own properties, it can be a Sub with no return value. However, if Construct returns Me after filling in the parameters, then the Factory methods/functions in the top 2 answers could leave the parameters to Construct . For example, using a factory class with this adjustment could go: Set Sue = Factory.NewEmployee.Construct("Sue", "50") , where NewEmployee is a method of Factory that returns a blank new Employee, but Construct is a method of Employee that assigns the parameters internally and returns Me .

Экземпляр класса, объект, создается с помощью ключевого слова New . Задачи инициализации зачастую необходимо выполнять на новых объектах до их использования. К распространенным задачам инициализации относится открытие файлов, подключение к базам данных и чтение значений параметров реестра. Visual Basic управляет инициализацией новых объектов с помощью процедур, называемых конструкторами (специальные методы, позволяющие контролировать инициализацию).

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

Использование конструкторов и деструкторов

Конструкторы и деструкторы управляют созданием и уничтожением объектов. Процедуры Sub New в Visual Basic инициализации Sub Finalize и уничтожения объектов; они заменяют Class_Initialize методы, Class_Terminate используемые в Visual Basic 6.0 и более ранних версиях.

Конструктор Sub New

Конструктор Sub New может быть запущен только один раз при создании класса. Его нельзя вызвать явным образом нигде, кроме первой строки кода другого конструктора этого же класса или производного класса. Более того, код метода Sub New всегда выполняется до любого другого кода в классе. Visual Basic неявно создает Sub New конструктор во время выполнения, если явно не определена Sub New процедура для класса.

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

Конструкторы часто перегружены, как в следующем коде:

При вызове класса, производного от другого класса, первая строка конструктора должна представлять собой вызов конструктора базового класса (кроме случаев, когда в базовом классе есть доступный конструктор, не принимающий параметры). Вызов базового класса, содержащего указанный выше конструктор, может быть, к примеру, таким MyBase.New(s) . MyBase.New В противном случае является необязательным, а среда выполнения Visual Basic вызывает ее неявно.

После написания кода для вызова конструктора родительского объекта можно добавить дополнительный код инициализации к процедуре Sub New . Sub New может принимать аргументы при вызове в качестве конструктора с параметрами. Эти параметры передаются из процедуры, вызывающей конструктор, например, Dim AnObject As New ThisClass(X) .

Sub Finalize

Перед высвобождением объектов среда CLR автоматически вызывает метод Finalize для объектов, определяющих процедуру Sub Finalize . Метод Finalize может содержать код, который необходимо выполнить непосредственно перед уничтожением объекта, например, код для закрытия файлов и сохранения информации о состоянии. Существует небольшой спад производительности при выполнении Sub Finalize , поэтому метод Sub Finalize нужно определять только в тех случаях, когда требуется явное высвобождение объектов.

Сборщик мусора в среде CLR не удаляет неуправляемые объекты, которые операционная система выполняет напрямую за пределами среды CLR. Причина состоит в том, что разные неуправляемые объекты следует уничтожать по-разному. Эта информация не связана напрямую с неуправляемым объектом, ее необходимо найти в документации по объектам. Класс, использующий неуправляемые объекты, должен удалить их в своем методе Finalize .

Деструктор Finalize является защищенным методом, который можно вызвать только из класса, к которому он принадлежит, или из производного класса. Система автоматически вызывает Finalize при уничтожении объекта, поэтому не следует явным образом вызывать Finalize извне реализации Finalize производного класса.

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

Как методы New и Finalize работают в иерархии классов

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

Когда создается экземпляр производного класса, конструктор Sub New базового класса выполняется в первую очередь, а затем — конструкторы в производных классах. Это происходит, поскольку первая строка кода в конструкторе Sub New использует синтаксис MyBase.New() для вызова конструктора класса на один уровень выше себя в иерархии классов. Затем конструктор Sub New вызывается для каждого класса в иерархии вплоть до достижения базового класса. На этом этапе выполняется код в конструкторе базового класса, а затем выполняется код в каждом конструкторе всех производных классов; код в производном классе самого дальнего уровня выполняется последним.

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

Интерфейс IDisposable

Экземпляры классов зачастую управляют ресурсами, которыми не управляет среда CLR, такими как дескрипторы Windows и подключения к базам данных. От этих ресурсов нужно избавляться в методе Finalize класса, поэтому они будут высвобождаться при уничтожении объекта сборщиком мусора. Тем не менее, сборщик мусора уничтожает объекты только в тех случаях, когда среде CLR нужно больше свободной памяти. Это означает, что ресурсы могут быть не высвобождены еще в течение долгого времени после того, как объект выйдет из области.

Чтобы дополнить сборку мусора, ваши классы могут предоставлять механизм активного управления системными ресурсами путем реализации интерфейса IDisposable. В IDisposable существует метод Dispose, который клиенты должны вызывать по завершении использования какого-либо объекта. Можно использовать метод Dispose для немедленного высвобождения ресурсов и выполнения таких задач как закрытие файлов и подключений к базам данных. В отличие от деструктора Finalize , метод Dispose не вызывается автоматически. Клиенты класса должны явным образом вызвать Dispose, когда нужно немедленно высвободить ресурсы.

Использование IDisposable

Класс, реализующий интерфейс IDisposable, должен включать следующие разделы кода:

Поле, чтобы отслеживать, уничтожен ли объект:

Перегрузка Dispose для высвобождения ресурсов класса. Этот метод должен вызываться методами Dispose и Finalize базового класса:

Реализация Dispose, содержащая только следующий код:

Переопределение метода Finalize , содержащее только следующий код:

Производное создание от класса, реализующего IDisposable

Классу, производному от базового класса, реализующего интерфейс IDisposable, нет необходимости переопределять какие-либо базовые методы, если только производный класс не использует дополнительные ресурсы, которые следует высвобождать. В этом случае производный класс должен переопределять метод Dispose(disposing) базового класса, чтобы удалить ресурсы производного класса. Это переопределение должно вызвать метод Dispose(disposing) базового класса.

Производный класс не должен переопределять методы Dispose и Finalize базового класса. Когда эти методы вызываются из экземпляра производного класса, реализация этих методов в базовом классе вызывает переопределение метода Dispose(disposing) производного класса.

Сбор мусора и деструктор Finalize

CLR периодически уничтожает объекты, если система определяет, что эти объекты больше не нужны. Объекты высвобождаются быстрее при нехватке системных ресурсов и медленнее в других случаях. Задержка между потерей объектом области и высвобождением объекта средой CLR означает, что в отличие от объектов в Visual Basic 6.0 и более ранних версиях, невозможно точно определить, когда объект будет уничтожен. В такой ситуации объекты, как говорят, имеют недетерминированное время существования. В большинстве случаев неопределенное время жизни не влияет на написание приложений, если помнить о том, что деструктор Finalize может быть выполнен не сразу после потери объектом области.

Еще одно отличие от систем сборки мусора заключается в использовании Nothing . Чтобы воспользоваться подсчетом ссылок в Visual Basic 6.0 и более ранних версиях, программисты часто назначали Nothing переменным объектов, чтобы высвобождать ссылки, удерживаемые этими переменными. Если переменная содержала последнюю ссылку на объект, ресурсы объекта были немедленно высвобождены. В более поздних версиях Visual Basic, хотя по-прежнему могут быть случаи, когда эта процедура еще применима, ее выполнение больше не приводит к немедленному высвобождению ресурсов объектом. Чтобы немедленно высвободить ресурсы, используйте метод объекта Dispose, если он доступен. Для переменной следует устанавливать значение Nothing лишь в тех случаях, когда ее время жизни достаточно велико по отношению ко времени, за которое сборщик мусора обнаруживает потерянные объекты.

I wonder, whether it is possible to create class-methods in VBA. By class-method I mean methods that can be called without having an object of the class. The 'static'-keyword does that trick in C++ and Java.

In the example below, I try to create a static factory method.

This doesn't cover the point of this question, which is class methods, but wouldn't most uses of a class method be covered by a method that returns a ref to another instance of the person class? e.g. In module you would have: Dim person As new Person: Set person = person.Create("Bob"). And the Create method in the class would be like: Public Function Create(name As String) As Person: Dim p As Person: Set p = New Person: p.name = name: Set create = p: End Function. I.e. once you have initialised a "handler" instance of the class, it can be used to return other instances of its own class.

@Cor_Blimey: The technique that you describe would surely work. However, the point of the question to get away without needing a "handler" instance.

8 Answers 8

1. Create a normal class containing the public method(s) you need to be 'static'

2. Include a public method [in this 'static' class] that initialises the [private] 'static fields' within the class (it can take parameters if you wish)

3. Create a module acts as a factory

4. you can now use the 'static' class by calling CreateStaticClass('parameters').MethodName('parameters') there is no need to initialise the instance as that is done by the factory method

6. To assign the singleton:

7. To use the singleton:

i was looking for how to write a singleton in excel vba. Google brought me to your answer. Thank you.

With your solution (if I understood well), I propose here a basic example how to define a "static" counter of instances i.e. +1 at each New .

There is no way to define Class Methods in VBA (or VB). I'd suggest to create a public function in a module.

You could try setting the VB_PredeclaredId attribute of the class you wish to be static to True . This creates a default instance of the class in much the same way that forms work in VBA (notice that you can refer to them directly without creating an instance. I know that this is not best practice but it is possible).

This means that you would have more of a singleton-style class, but it could serve your requirements.

You can't set this directly from the VBA IDE itself, however, you can perform the following steps:

1. Export the class you wish to make static to a folder.

2. Open the .cls file you exported in your favourite text editor and change the entry for VB_PredeclaredId so that it reads VB_PredeclaredId = True .

3. Save the file and re-import into VBA.

You should then be able to call your public methods on the class without having to instantiate the class. Bear in mind that the Initialize method is only called the first time you execute a class method/access a class property, and Terminate method is never called. Therefore you may wish to write your own constructor and also ensure you explicitly call the destructor if you need one.

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

Отображаемые на компьютере имена или расположения некоторых элементов пользовательского интерфейса Visual Studio могут отличаться от указанных в следующих инструкциях. Это зависит от имеющегося выпуска Visual Studio и используемых параметров. Дополнительные сведения см. в разделе Персонализация среды IDE.

Определение класса

Создайте проект, щелкнув "Создать Project" в меню "Файл". Откроется диалоговое окно Создание проекта .

Выберите Windows приложение из списка шаблонов проектов Visual Basic, чтобы отобразить новый проект.

Добавьте новый класс в проект, нажав кнопку "Добавить класс" в меню Project. Откроется диалоговое окно Добавление нового элемента.

Выберите шаблон класса .

Присвойте новому классу UserNameInfo.vb имя и нажмите кнопку "Добавить ", чтобы отобразить код для нового класса.

Редактор кода Visual Basic можно использовать для добавления класса в форму запуска, введя ключевое Class слово, за которым следует имя нового класса. Редактор кода предоставляет для вас соответствующую End Class инструкцию.

Определите частное поле для класса, добавив следующий код между Class операторами и End Class операторами:

Объявление поля как Private означает, что его можно использовать только в классе. Поля можно сделать доступными из-за пределов класса с помощью модификаторов доступа, таких как Public предоставление большего доступа. Дополнительные сведения см. в разделе "Уровни доступа" в Visual Basic.

Определите свойство для класса, добавив следующий код:

Определите метод для класса, добавив следующий код:

Определите параметризованный конструктор для нового класса, добавив процедуру с именем Sub New :

Sub New Конструктор вызывается автоматически при создании объекта на основе этого класса. Этот конструктор задает значение поля, которое содержит имя пользователя.

Создание кнопки для тестирования класса

Измените форму запуска на режим конструктора, щелкнув правой кнопкой мыши его имя в Обозреватель решений и выбрав конструктор представлений. По умолчанию форма запуска для проектов приложений Windows называется Form1.vb. Затем появится основная форма.

Добавьте кнопку в основную форму и дважды щелкните ее, чтобы отобразить код обработчика Button1_Click событий. Добавьте следующий код для вызова процедуры тестирования:

Запуск приложения

Читайте также: