VBA:一个Sub的const参数 – 避免修改通过值传递的参数的值

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

在VBA中实现相同的build议(即最简单,最便携等)的方式是什么?

问题是关于下面的项目3。 有一个被接受的答案,但是问题是可以select的(在这方面可能没有最后一个字)。

编辑澄清的functionVBA与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);作为补充mysub(&j); ,也就是说,这也是为了这个工作的来电者。
    Sub可以修改被调用Sub内部的“variables”( i ,在VBA中; *i ,在C中)的值。 如果是这样,它会自动修改调用者的值。

  2. 按价值传递
    在VBA中Sub mysub(ByVal i as Integer) ,它根据来自调用者(使用Call mysub(j)的值获取参数的值,而不知道mysub是否需要ByValByRef )对应于
    在C中void mysub(int i);
    Sub可以修改被调用的Sub内的“variables”( i ,在VBA或C中)的值。 如果是这样,它不会影响调用者的值。

  3. 按照价值传递,用const限定
    在C中void mysub(const int i);
    Sub不能修改被调用Sub内的“variables”( i在C中)的值。 当然,调用者的价值也没有任何变化。

VBA不是为了防止参数在函数内被修改而devise的。 如果你想这样做,你必须自己实施这些保护作为解决方法。 下面是关于如何在VBA中实现const保护的两点build议。

OP的最终解决scheme可以在这里find。


使用让和保护参数

下面是一个可以用来阻止函数使用Let和Get Properties来修改类的成员variables的方法的例子。

用下面的代码创build一个类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 

用这些函数创build一个正常的代码模块:

 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()函数来testingclsPerson类。 这是一种基于状态的运行时方法,用于防止类的成员被修改,可以打开和closures。 每个属性让clsPerson检查LockMemberVariables是否为true,如果是,则尝试更改其值时抛出错误。 如果您要修改代码,以便Bob.LockMemberVariables = False ,然后调用AlterPerson,则可以毫无错误地修改该名称。

用接口保护参数

另一种可能实现保护的方式是通过接口。 事实上,你可能更喜欢这个,因为它在编译时工作。 假设你创build一个名为IProtectedPerson的接口(类), 它只支持Get成员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 and 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 

然后你会创build一个以IProtectedPerson作为参数的函数。 如果您尝试对私有成员variables进行赋值,代码将无法编译,因为Let函数在该接口上不可用:

 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。

尝试这个:

 Sub mysub(ByVal i As Integer) End Sub 

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

重要文本:当你定义一个过程时,你有两个关于如何传递参数的select:通过引用或值。 当一个variables通过引用传递给一个过程时,VBA实际上将variables的内存地址传递给过程,然后可以直接对其进行修改。 当执行返回到调用过程时,该variables包含修改的值。 当参数按值传递时,VBA将该variables的一个副本传递给过程。 程序然后修改副本,并且variables的原始值保持不变; 当执行返回到调用过程时,该variables包含与传递之前相同的值。

编辑:

我现在明白你想防止被调用的子例程中的variables被修改,而不是调用者子。 它不可能按照你的build议。 VBA只有传递参数的byval和byref。 你可以尝试这样的事情(这不是完全的certificate):

在名为: 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 

testing:

 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 

这个答案是基于对这个问题的另一个很好的回答,在这里被称为BH 。

使用Let和Get保护参数 ( 在回答BH中search )应该如下使用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_const2的其余部分也是有效的。

保护参数与接口 ( 在答案BH中search)中的常量子查询似乎完全按照预期工作。

结论

似乎有可能模仿const行为的方式。 代价是不得不编写额外的代码,并为每个想要使用的consttypes定义新的类。

PS:那么const对象的某些成员又是其他对象呢?