传递函数参数构造函数,VBA(Pass arguments to Constructor in VB

2019-07-21 01:10发布

你怎么能建立直接传递参数给自己的类的对象?

事情是这样的:

Dim this_employee as Employee
Set this_employee = new Employee(name:="Johnny", age:=69)

不能够做到这一点是很烦人,你最终脏的解决方案来解决这个周围。

Answer 1:

下面是我使用的是最近一个小技巧,带来了良好的效果。 我想与那些谁拥有与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

这是更好的。



Answer 2:

我用一个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

不幸的是,对象的新实例被创建每次,所以任何静态变量将在执行后会丢失。 线和在这个伪静态方法中使用的任何其他静态变量的集合必须在模块中进行定义。



Answer 3:

当您导出类模块,并用记事本打开该文件,你会发现,接近顶部,一堆的隐藏属性(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接口,并享受不变,多态对象。



Answer 4:

使用技巧

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)的私有成员唯一的问题是,如果呼吁非静态实例将重新初始化私有成员。 但是,可以通过设置一个标志来避免。



Answer 5:

另一种方法

假设你创建一个类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(...)

唯一的代价是额外的电话,但好处是,你可以把一切都在类模块中,和调试变得更加容易。



文章来源: Pass arguments to Constructor in VBA