VBA:Sub的const参数 - 避免修改由value传递的参数的值

时间:2013-11-21 20:18:16

标签: excel vba excel-vba arguments const

我的意思是将参数传递给VBA Sub,确保它不被修改。我会在C中做void mysub( const int i );

如果有的话,在VBA中推荐(即最简单,最便携等)的方法是什么?

问题是关于下面的第3项。有一个公认的答案,但问题是对替代方案持开放态度(可能没有最后的说法)。

修改 根据答案和评论,澄清功能VBA与C的等效性:

  1. 通过引用传递
    在VBA中Sub mysub(i as Integer)(默认值,或Sub mysub(ByRef i as Integer)),它通过引用获取参数,而无需来自调用方的“请求权限”(使用Call mysub(j)) ,在C.中没有确切的等价物 在C 中,最接近的是void mysub(int * i);。但是在C语言中,必须通过mysub(&j);调用来补充这一点,也就是说,调用者也可以使用它。 Sub可以修改被调用i内的“变量”(*i,在VBA中; Sub,在C中)的值。如果是,则会自动修改调用者中的值。

  2. 按值传递
    在VBA中Sub mysub(ByVal i as Integer),它使用来自调用方(使用Call mysub(j)的值的参数,甚至不知道mysub是否需要ByVal或{ {1}}),对应于
    在C 中,ByRef
    void mysub(int i);可以修改被调用Sub内的“变量”(i,在VBA或C中)的值。如果是,则不会影响调用者的值。

  3. 按值传递,符合Sub
    在C 中,const
    void mysub(const int i);无法修改被调用Sub内的“变量”(i,在C中)的值。当然,调用者的值也没有任何变化。

3 个答案:

答案 0 :(得分:5)

试试这个:

Sub mysub(ByVal i As Integer)


End Sub

http://msdn.microsoft.com/en-us/library/office/aa164263(v=office.10).aspx

重要文本:定义过程时,您有两种关于如何将参数传递给它的选择:通过引用或按值。当变量通过引用传递给过程时,VBA实际上将内存中变量的地址传递给过程,然后过程可以直接修改它。当执行返回到调用过程时,该变量包含修改后的值。 当参数按值传递时,VBA会将变量的副本传递给过程。然后,该过程修改副本,变量的原始值保持不变;当执行返回到调用过程时,该变量包含与传递之前相同的值。

修改

我现在明白你想要阻止在被调用的子例程中修改变量,而不是调用者子句。它不可能按照你的建议。 VBA只有byval和byref来传递参数。你可以试试这样的东西(这不是完全证据):

在名为: ConstInteger

的类模块中
Private i As Variant
Public Property Get Value() As Integer
    Value = i
End Property
Public Property Let Value(Value As Integer)
    If IsEmpty(i) Then i = Value
End Property

测试:

Sub Caller()
    Dim clsInt As New ConstInteger
    clsInt.Value = 1
    Call Called(clsInt)
End Sub
Sub Called(clsInt As ConstInteger)
    Debug.Print clsInt.Value
    clsInt.Value = 2
    Debug.Print clsInt.Value
End Sub

答案 1 :(得分:3)

VBA并非旨在防止在函数内部修改参数。如果你想这样做,你必须自己实施这些保护作为一种解决方法。以下是关于如何在VBA中实现const保护的两个建议。

可以找到OP的最终解决方案here


使用Let和Get保护参数

以下是使用Let和Get Properties阻止函数修改类的成员变量的方法示例。

使用以下代码创建一个类clsPerson:

Option Explicit

Private m_strName As String
Private m_intAge As Integer
Private m_strAddress As String

Public LockMemberVariables As Boolean

Public Property Get Name() As String
    Name = m_strName
End Property

Public Property Let Name(strName As String)
    If Not LockMemberVariables Then
        m_strName = strName
    Else
        Err.Raise vbObjectError + 1, , "Member variables are locked!"
    End If
End Property

Public Property Get Age() As Integer
    Age = m_intAge
End Property

Public Property Let Age(intAge As Integer)
    If Not LockMemberVariables Then
        m_intAge = intAge
    Else
        Err.Raise vbObjectError + 1, , "Member variables are locked!"
    End If
End Property

Public Property Get Address() As String
    Address = m_strAddress
End Property

Public Property Let Address(strAddress As String)
    If Not LockMemberVariables Then
        m_strAddress = strAddress
    Else
        Err.Raise vbObjectError + 1, , "Member variables are locked!"
    End If
End Property

使用以下函数创建普通代码模块:

Option Explicit

Public Sub Main()
    Dim Bob As clsPerson
    Set Bob = New clsPerson

    Bob.Name = "Bob"
    Bob.Age = 30
    Bob.Address = "1234 Anwhere Street"

    Bob.LockMemberVariables = True

    PrintPerson Bob
    AlterPerson Bob

End Sub

Public Sub PrintPerson(p As clsPerson)
    MsgBox "Name: " & p.Name & vbCrLf & _
        "Age: " & p.Age & vbCrLf & _
        "Address: " & p.Address & vbCrLf
End Sub

Public Sub AlterPerson(p As clsPerson)
    p.Name = "Jim"
End Sub

运行Main()函数来测试clsPerson类。这是一个基于状态的运行时方法,用于防止可以打开和关闭的类成员被修改。 clsPerson中的每个Property Let检查LockMemberVariables是否为true,如果是,则在尝试更改其值时抛出错误。如果您要修改代码以便Bob.LockMemberVariables = False然后调用AlterPerson,则可以修改名称而不会出现错误。

使用界面保护参数

您可以通过接口实现保护的另一种方式。实际上,您可能更喜欢这个,因为它在编译时工作。假设您创建了一个名为IProtectedPerson的接口(类),仅支持Get for the members variables

Public Property Get Name() As String

End Property

Public Property Get Age() As Integer

End Property

Public Property Get Address() As String

End Property

然后你重写了你的clsPerson类,使它具有正常的Let和Get,但也实现了IProtectedPerson接口:

Option Explicit

Implements IProtectedPerson

Private m_strName As String
Private m_intAge As Integer
Private m_strAddress As String

Public Property Get Name() As String
    Name = m_strName
End Property

Public Property Let Name(strName As String)
    m_strName = strName
End Property

Public Property Get Age() As Integer
    Age = m_intAge
End Property

Public Property Let Age(intAge As Integer)
    m_intAge = intAge
End Property

Public Property Get Address() As String
    Address = m_strAddress
End Property

Public Property Let Address(strAddress As String)
    m_strAddress = strAddress
End Property

'IProtectedPerson Interface Implementation

Private Property Get IProtectedPerson_Name() As String
    IProtectedPerson_Name = Me.Name
End Property

Private Property Get IProtectedPerson_Age() As Integer
    IProtectedPerson_Age = Me.Age
End Property

Private Property Get IProtectedPerson_Address() As String
    IProtectedPerson_Address = Me.Address
End Property

然后,您将创建将IProtectedPerson作为参数的函数。如果您尝试对私有成员变量进行赋值,则代码将无法编译,因为该函数在该接口上不可用:

Option Explicit

Public Sub Main()
    Dim Bob As clsPerson
    Set Bob = New clsPerson

    Bob.Name = "Bob"
    Bob.Age = 30
    Bob.Address = "1234 Anwhere Street"

    AlterPerson Bob
    AlterProtectedPerson Bob
End Sub

Public Sub AlterPerson(p As clsPerson)
    p.Name = "Jim"
End Sub

Public Sub AlterProtectedPerson(p As IProtectedPerson)
    p.Name = "Sally"
End Sub

代码无法编译,因为AlterProtectedPerson函数中的p没有Property Let for Name。

答案 2 :(得分:0)

此答案基于another excellent answer to this question, here called BH

使用Let and Get 保护参数search in the answer BH)的const替代品应如下使用。 在一个作为called_const(const clsInt As clsPerson)的函数中(按照答案BH中的示例),可以添加

Sub called_const(clsInt As clsPerson)
    clsInt.LockMemberVariables = True
    <Functionality of the Sub>
    clsInt.LockMemberVariables = False
End Sub

然后,如果called_const中的某些代码违反const处方,则会产生运行时错误(而不是C的情况下的编译错误)。 我可以想到一个(可能不常见的)这种情况会失败的情况。当从Sub调用另一个类似called_const2(const clsInt As clsPerson)(例如,called_const)时。然后,在called_const2结束时执行的解锁对called_const的剩余部分也有效。

保护带有界面的参数search in the answer BH)的const替代品似乎与预期完全一致。

<强>结论

似乎有可能模仿const行为。成本是必须编写额外的代码,并为每个想要使用的const类型定义新的类。

PS:const个对象的某些成员又是其他对象的情况怎么样?