VBA의 생성자에 인수 전달
자신의 클래스에 직접 인수를 전달하는 객체를 어떻게 구성 할 수 있습니까?
이 같은:
Dim this_employee as Employee
Set this_employee = new Employee(name:="Johnny", age:=69)
이것을 할 수 없다는 것은 매우 성가신 일이며 결국에는이 문제를 해결할 수있는 더러운 솔루션이 생깁니다.
여기 내가 최근에 사용하고 좋은 결과를 가져 오는 약간의 트릭이 있습니다. VBA와 자주 싸워야하는 분들과 함께 나누고 싶습니다.
1.- 각 사용자 정의 클래스에서 공개 시작 서브 루틴을 구현하십시오. 모든 수업에서 InitiateProperties라고 부릅니다. 이 메서드는 생성자에게 보낼 인수를 받아야합니다.
2.- factory라는 모듈을 만들고 "Create"라는 단어와 클래스와 동일한 이름, 생성자가 필요로하는 동일한 들어오는 인수를 사용하여 공용 함수를 만듭니다. 이 함수는 클래스를 인스턴스화하고 (1)에서 설명한 시작 서브 루틴을 호출하여 수신 된 인수를 전달해야합니다. 마지막으로 인스턴스화되고 시작된 메서드를 반환했습니다.
예:
사용자 정의 클래스 Employee가 있다고 가정 해 보겠습니다. 이전 예 에서처럼 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 (arguments) , factory.CreateClassB (other_arguments) 등 을 호출하여 인스턴스화하십시오 .
편집하다
stenci가 지적했듯이 생성자 함수에서 지역 변수를 생성하는 것을 피함으로써 간결한 구문으로 동일한 작업을 수행 할 수 있습니다. 예를 들어 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
합니다.
예를 들어 Point
클래스 :
Class Point
Private X, Y
Sub Init(X, Y)
Me.X = X
Me.Y = Y
End Sub
의 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
함수는 객체 내부에서 정의됩니다.
편집하다
이것은 정적 메서드를 만들 수 있다는 것을 추가하는 것을 잊었습니다. 예를 들어 다음과 같은 작업을 수행 할 수 있습니다 (매개 변수를 선택적으로 만든 후).
NewLine.DeleteAllLinesShorterThan 10
불행히도 개체의 새 인스턴스는 매번 생성되므로 실행 후 모든 정적 변수가 손실됩니다. 이 의사 정적 메서드에 사용되는 줄 모음 및 기타 정적 변수는 모듈에서 정의해야합니다.
클래스 모듈을 내보내고 메모장에서 파일을 열면 맨 위에 숨겨진 속성이 많이 있습니다 (VBE는 속성을 표시하지 않으며 대부분의 기능을 조정하는 기능도 노출하지 않습니다). 그중 하나는 다음과 VB_PredeclaredId
같습니다.
Attribute VB_PredeclaredId = False
로 설정하고 True
, 저장하고, 모듈을 VBA 프로젝트로 다시 가져옵니다.
가있는 클래스에는 PredeclaredId
무료로 얻을 수있는 "전역 인스턴스"가 있습니다. UserForm
모듈 과 똑같습니다 (사용자 양식을 내 보내면 predeclaredId 속성이 true로 설정된 것을 볼 수 있습니다).
많은 사람들이 기꺼이 미리 선언 된 인스턴스를 사용하여 상태를 저장합니다. 그것은 잘못된 것입니다. 그것은 정적 클래스에 인스턴스 상태를 저장하는 것과 같습니다!
대신 해당 기본 인스턴스를 활용하여 팩토리 메서드를 구현합니다.
[ 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
인스턴스를 변경할 수 있습니다 .
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
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:
Dim empl As IEmployee
Set empl = Employee.Create("Immutable", 42)
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.
Using the trick
Attribute VB_PredeclaredId = True
I found another more compact way:
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
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.
Another approach
Say you create a class clsBitcoinPublicKey
In the class module create an ADDITIONAL subroutine, that acts as you would want the real constructor to behave. Below I have named it 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(...)
The only penalty is the extra call, but the advantage is that you can keep everything in the class module, and debugging becomes easier.
Why not this way:
- In a class module »myClass« use
Public Sub Init(myArguments)
instead ofPrivate Sub Class_Initialize()
- Instancing:
Dim myInstance As New myClass: myInstance.Init myArguments
참고URL : https://stackoverflow.com/questions/15224113/pass-arguments-to-constructor-in-vba
'program story' 카테고리의 다른 글
CSS 3을 사용하여 세로로 정렬 (0) | 2020.10.26 |
---|---|
Select2 Ajax 방법이 선택되지 않음 (0) | 2020.10.26 |
Pandas를 사용하여 문자열 열의 각 값에 문자열 접두사 추가 (0) | 2020.10.26 |
새로운 Java 8 날짜 시간 API에 나노초 정밀도가없는 이유는 무엇입니까? (0) | 2020.10.26 |
Eclipse에서 내 gradle 종속성 업데이트 (0) | 2020.10.25 |