Передать аргументы конструктору в VBA
Как вы можете построить объекты, передающие аргументы непосредственно в ваши собственные классы?
что-то вроде этого:
Dim this_employee as Employee
Set this_employee = new Employee(name:="Johnny", age:=69)
Не в состоянии сделать это очень раздражает, и вы в конечном итоге с грязными решениями, чтобы обойти это.
5 ответов:
вот небольшой трюк, который я использую в последнее время и приносит хорошие результаты. Я хотел бы поделиться с теми, кому приходится часто бороться с VBA.
1.- реализовать общедоступную подпрограмму инициализации в каждом из ваших пользовательских классов. Я называю это InitiateProperties во всех моих классах. Этот метод должен принимать аргументы, которые вы хотите отправить конструктору.
2.- создайте модуль под названием factory и создайте публичную функцию с помощью слово "Create" плюс то же имя, что и класс, и те же входящие аргументы, что и конструктор. Эта функция должна создать экземпляр класса и вызвать подпрограмму инициализации, описанную в пункте (1), передав полученные аргументы. Наконец, возвращается экземпляр и инициированный метод.
пример:
допустим, у нас есть сотрудник пользовательского класса. Как и в предыдущем примере, is должен быть создан с именем и возрастом.
это Метод InitiateProperties. m_name и m_age - это наши частные свойства, которые должны быть установлены.
Public Sub InitiateProperties(name as String, age as Integer) m_name = name m_age = age End Subа теперь в Заводском модуле:
Public Function CreateEmployee(name as String, age as Integer) as Employee Dim employee_obj As Employee Set employee_obj = new Employee employee_obj.InitiateProperties name:=name, age:=age set CreateEmployee = employee_obj End Functionи, наконец, когда вы хотите создать экземпляр сотрудника
Dim this_employee as Employee Set this_employee = factory.CreateEmployee(name:="Johnny", age:=89)особенно полезно, когда у вас есть несколько классов. Просто разместите функцию для каждого в модуле factory и создайте экземпляр, просто вызвав фабрики.CreateClassA(аргументы),фабрики.CreateClassB (other_arguments), так далее.
EDIT
как указал stenci, вы можете сделать то же самое с синтаксисом terser, избегая создавать локальную переменную в функциях конструктора. Например, функция CreateEmployee может быть написана следующим образом:
Public Function CreateEmployee(name as String, age as Integer) as Employee Set CreateEmployee = new Employee CreateEmployee.InitiateProperties name:=name, age:=age End Functionчто приятнее.
я использую один
Factoryмодуль, который содержит один (или более) конструктор за класс, который вызываетInitчлены каждого класса.например a
Pointкласс:Class Point Private X, Y Sub Init(X, Y) Me.X = X Me.Y = Y End SubA
LineклассClass Line Private P1, P2 Sub Init(Optional P1, Optional P2, Optional X1, Optional X2, Optional Y1, Optional Y2) If P1 Is Nothing Then Set Me.P1 = NewPoint(X1, Y1) Set Me.P2 = NewPoint(X2, Y2) Else Set Me.P1 = P1 Set Me.P2 = P2 End If End Subи
Factoryмодуль:Module Factory Function NewPoint(X, Y) Set NewPoint = New Point NewPoint.Init X, Y End Function Function NewLine(Optional P1, Optional P2, Optional X1, Optional X2, Optional Y1, Optional Y2) Set NewLine = New Line NewLine.Init P1, P2, X1, Y1, X2, Y2 End Function Function NewLinePt(P1, P2) Set NewLinePt = New Line NewLinePt.Init P1:=P1, P2:=P2 End Function Function NewLineXY(X1, Y1, X2, Y2) Set NewLineXY = New Line NewLineXY.Init X1:=X1, Y1:=Y1, X2:=X2, Y2:=Y2 End Functionодним из приятных аспектов этого подхода является то, что позволяет легко использовать заводские функции внутри выражений. Например можно что-то сделать например:
D = Distance(NewPoint(10, 10), NewPoint(20, 20)или:
D = NewPoint(10, 10).Distance(NewPoint(20, 20))это чисто: завод делает очень мало, и он делает это последовательно по всем объектам, только создание и один
Initпризываем каждого создатель.и это довольно объектно-ориентированный:
Initфункции определяются внутри объектов.EDIT
Я забыл добавить, что это позволяет мне создать статические методы. Например, я могу сделать что-то подобное (сделав параметры необязательными):
NewLine.DeleteAllLinesShorterThan 10к сожалению, каждый раз создается новый экземпляр объекта, поэтому любая статическая переменная будет потеряна после выполнения. Набор строк и любая другая статическая переменная, используемая в этом псевдостатическом методе, должны быть определены в модуле.
когда вы экспортируете модуль класса и открываете файл в блокноте, вы заметите, что в верхней части есть куча скрытых атрибутов (VBE не отображает их и не предоставляет функциональность для настройки большинства из них). Один из них -
VB_PredeclaredId:Attribute VB_PredeclaredId = Falseустановить
Trueсохраните и повторно импортировать модуль в проект VBA.классы с a
PredeclaredIdесть "глобальный экземпляр", который вы получаете бесплатно - точно так же, какUserFormмодули (экспорт формы пользователя, вы увидите, что его атрибут preforlaredid установлен в true).многие люди просто счастливо используют экземпляр predeclared для хранения состояния. Это неправильно - это как хранить состояние экземпляра в статическом классе!
вместо этого вы используете этот экземпляр по умолчанию для реализации своего заводского метода:
[
Employeeкласса]'@PredeclaredId Option Explicit Private Type TEmployee Name As String Age As Integer End Type Private this As TEmployee Public Function Create(ByVal emplName As String, ByVal emplAge As Integer) As Employee With New Employee .Name = emplName .Age = emplAge Set Create = .Self 'returns the newly created instance End With End Function Public Property Get Self() As Employee Set Self = Me End Property Public Property Get Name() As String Name = this.Name End Property Public Property Let Name(ByVal value As String) this.Name = value End Property Public Property Get Age() As String Age = this.Age End Property Public Property Let Age(ByVal value As String) this.Age = value End PropertyС этим, вы можете сделать это:
Dim empl As Employee Set empl = Employee.Create("Johnny", 69)
Employee.Createработает с по умолчанию экземпляр, то есть он считается членом тип, и вызывается только из экземпляра по умолчанию.проблема в том, что это также совершенно законно:
Dim emplFactory As New Employee Dim empl As Employee Set empl = emplFactory.Create("Johnny", 69)и это отстой, потому что теперь у вас есть запутанный API. Вы могли бы использовать
'@Descriptionаннотации /VB_Descriptionатрибуты для использования документа, но без Rubberduck нет ничего в редакторе, который показывает вам эту информацию на сайтах вызовов.кроме того,
Property Letчлены доступны, так что вашEmployeeэкземпляр mutable:empl.Name = "Booba" ' Johnny no more!фокус в том, чтобы сделать свой класс реализовать интерфейс это только выставляет то, что должно быть выставлено:
[
IEmployeeкласса]Option Explicit Public Property Get Name() As String : End Property Public Property Get Age() As Integer : End Propertyи
EmployeeреализоватьIEmployee- последний класс может выглядеть так:[
Employeeкласса]'@PredeclaredId Option Explicit Implements IEmployee Private Type TEmployee Name As String Age As Integer End Type Private this As TEmployee Public Function Create(ByVal emplName As String, ByVal emplAge As Integer) As IEmployee With New Employee .Name = emplName .Age = emplAge Set Create = .Self 'returns the newly created instance End With End Function Public Property Get Self() As IEmployee Set Self = Me End Property Public Property Get Name() As String Name = this.Name End Property Public Property Let Name(ByVal value As String) this.Name = value End Property Public Property Get Age() As String Age = this.Age End Property Public Property Let Age(ByVal value As String) this.Age = value End Property Private Property Get IEmployee_Name() As String IEmployee_Name = Name End Property Private Property Get IEmployee_Age() As Integer IEmployee_Age = Age End Propertyобратите внимание на способ возвращает интерфейс, и интерфейс не разоблачение
Property Letчлены? Теперь вызывающий код может выглядеть так:Dim empl As IEmployee Set empl = Employee.Create("Immutable", 42)а так как клиентский код написан против интерфейса, то только члены
emplexposes-это члены, определенныеIEmployeeинтерфейс, что означает, что он не видитCreateметод, ниSelfгеттер, ниProperty Letмутаторы: так вместо работы с "бетоном"Employeeкласс, остальная часть кода может работать с "абстрактным"IEmployeeинтерфейс, и наслаждаться неизменным, полиморфным объектом.
используя хитрость
Attribute VB_PredeclaredId = TrueЯ нашел другой более компактный способ:
Option Explicit Option Base 0 Option Compare Binary Private v_cBox As ComboBox ' ' Class creaor Public Function New_(ByRef cBox As ComboBox) As ComboBoxExt_c If Me Is ComboBoxExt_c Then Set New_ = New ComboBoxExt_c Call New_.New_(cBox) Else Set v_cBox = cBox End If End Function
другой подход
скажем, вы создаете класс clsBitcoinPublicKey
в модуле class создайте дополнительную подпрограмму, которая действует так, как вы хотите, чтобы действительный конструктор вел себя. Ниже я назвал его ConstructorAdjunct.
Public Sub ConstructorAdjunct(ByVal ...) ... End Sub From the calling module, you use an additional statement Dim loPublicKey AS clsBitcoinPublicKey Set loPublicKey = New clsBitcoinPublicKey Call loPublicKey.ConstructorAdjunct(...)единственным наказанием является дополнительный вызов, но преимущество в том, что вы можете сохранить все в модуле класса, и отладка становится проще.
Comments