你怎么能建立直接传递参数给自己的类的对象?
事情是这样的:
Dim this_employee as Employee
Set this_employee = new Employee(name:="Johnny", age:=69)
不能够做到这一点是很烦人,你最终脏的解决方案来解决这个周围。
你怎么能建立直接传递参数给自己的类的对象?
事情是这样的:
Dim this_employee as Employee
Set this_employee = new Employee(name:="Johnny", age:=69)
不能够做到这一点是很烦人,你最终脏的解决方案来解决这个周围。
下面是我使用的是最近一个小技巧,带来了良好的效果。 我想与那些谁拥有与VBA经常吵架分享。
1.-实现每个自定义类的公共启动子程序。 我把它叫做InitiateProperties在我的所有类。 该方法具有接受你想发送给构造函数的参数。
2:创建一个名为工厂模块,并创建一个公共职能与单词“创建”,加上同名的类,和相同的传入参数作为构造的需求。 这个函数实例类,并调用子程序开始在点(1)中说明,通过接收到的参数。 最后返回的实例化和启动方法。
例:
比方说,我们有自定义类员工。 正如前面的例子,是要在与姓名和年龄进行实例化。
这是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(参数)实例,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
注意Create
方法现在返回的接口 ,该接口不公开Property Let
成员? 现在,调用代码可以是这样的:
Dim empl As IEmployee
Set empl = Employee.Create("Immutable", 42)
而且,由于客户端代码针对接口编写的,唯一的成员empl
自曝是由定义的成员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
正如你可以看到New_调用构造函数既创建和设置类(如INIT)的私有成员唯一的问题是,如果呼吁非静态实例将重新初始化私有成员。 但是,可以通过设置一个标志来避免。
另一种方法
假设你创建一个类clsBitcoinPublicKey
在类模块创建一个额外的子程序,充当你想真正的构造函数的行为。 下面我把它命名为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(...)
唯一的代价是额外的电话,但好处是,你可以把一切都在类模块中,和调试变得更加容易。