VBA:一个Sub的const参数 – 避免修改通过值传递的参数的值
我的意思是传递一个参数到一个VBA Sub
,确保它不被修改。 我会做在C中作为void mysub( const int i );
。
在VBA中实现相同的build议(即最简单,最便携等)的方式是什么?
问题是关于下面的项目3。 有一个被接受的答案,但是问题是可以select的(在这方面可能没有最后一个字)。
编辑澄清的functionVBA与C等效,需要根据答案和意见:
-
通过引用传递 。
在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中)的值。 如果是这样,它会自动修改调用者的值。 -
按价值传递 。
在VBA中 ,Sub mysub(ByVal i as Integer)
,它根据来自调用者(使用Call mysub(j)
的值获取参数的值,而不知道mysub
是否需要ByVal
或ByRef
)对应于
在C中 ,void mysub(int i);
。
Sub
可以修改被调用的Sub
内的“variables”(i
,在VBA或C中)的值。 如果是这样,它不会影响调用者的值。 -
按照价值传递,用
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
行为的方式。 代价是不得不编写额外的代码,并为每个想要使用的const
types定义新的类。
PS:那么const
对象的某些成员又是其他对象呢?