我的意思是将参数传递给VBA Sub
,确保它不被修改。我会在C中做void mysub( const int i );
。
如果有的话,在VBA中推荐(即最简单,最便携等)的方法是什么?
问题是关于下面的第3项。有一个公认的答案,但问题是对替代方案持开放态度(可能没有最后的说法)。
修改 根据答案和评论,澄清功能VBA与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);
调用来补充这一点,也就是说,调用者也可以使用它。
Sub
可以修改被调用i
内的“变量”(*i
,在VBA中; Sub
,在C中)的值。如果是,则会自动修改调用者中的值。
按值传递。
在VBA中,Sub mysub(ByVal i as Integer)
,它使用来自调用方(使用Call mysub(j)
的值的参数,甚至不知道mysub
是否需要ByVal
或{ {1}}),对应于
在C 中,ByRef
。
void mysub(int i);
可以修改被调用Sub
内的“变量”(i
,在VBA或C中)的值。如果是,则不会影响调用者的值。
按值传递,符合Sub
。
在C 中,const
。
void mysub(const int i);
无法修改被调用Sub
内的“变量”(i
,在C中)的值。当然,调用者的值也没有任何变化。
答案 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
个对象的某些成员又是其他对象的情况怎么样?