作为参数传递的对象 ByVal 应该如何表现 [英] How object passed as an argument ByVal should behave
问题描述
我正在研究/学习 ByVal 和 ByRef 在处理调用对象时的行为.所以我创建了这个类 PersonModel
I am studying/learning the bahavior of ByVal and ByRef when it comed to working with a call object. So I created this class PersonModel
Private Type TPerson
firstName As String
lastName As String
End Type
Private this As TPerson
Public Property Get firstName() As String
firstName = this.firstName
End Property
Public Property Let firstName(ByVal strNewValue As String)
this.firstName = strNewValue
End Property
Public Property Get lastName() As String
lastName = this.lastName
End Property
Public Property Let lastName(ByVal strNewValue As String)
this.lastName = strNewValue
End Property
Public Property Get fullName() As String
fullName = this.firstName & " " & this.lastName
End Property
并且我制作了一个标准模块,试图查看在 s 子例程中作为 ByVal 或 ByRef 传递的对象的值如何受到影响.这是标准模块中的代码
And I made a standard module trying to see how the value of an object be affected if it's passed as ByVal or ByRef in s subroutine. Here's the code in standard module
Private passedPerson As PersonModel
Public Sub StructureType()
Dim Object1 As PersonModel
Dim Object2 As PersonModel
Set Object1 = New PersonModel
With Object1
.firstName = "Max"
.lastName = "Weber"
Debug.Print .fullName 'gives Max Weber
End With
Set Object2 = Object1 'Object2 references Object1
Debug.Print Object2.fullName 'gives Max Weber
passByVal Object1
' First Call
Debug.Print passedPerson.fullName 'gives Max Weber
With Object2
.firstName = "Karl"
.lastName = "Marx"
Debug.Print .fullName 'gives Karl Marx
End With
'Second Call
Debug.Print passedPerson.fullName 'gives Karl Marx
End Sub
Private Sub passByVal(ByVal person As PersonModel)
Set passedPerson = New PersonModel
Set passedPerson = person
End Sub
我只是期望在代码的第二个调用部分 Debug.PrintpassedPerson.fullName
会给我一个不变的Max Weber"值.但相反,它赋予了新的价值卡尔·马克思".即使我将过程 passByVal 的代码更改为:
I was just expecting that in the second call part of the code Debug.Print passedPerson.fullName
will give me an unchanged value of "Max Weber". But instead, it's giving the new value "Karl Marx". Even if I change the code of the procedure passByVal to:
Private Sub passByVal(ByVal person As PersonModel)
Dim newPerson As PersonModel
Set newPerson = New PersonModel
Set newPerson = person
Set passedPerson = newPerson
End Sub
第二次调用部分代码 Debug.PrintpassedPerson.fullName
仍然给出Karl Marx".无论将 ByVal
更改为 ByRef
,它仍然给出相同的结果.
Second call part of the code Debug.Print passedPerson.fullName
is still giving "Karl Marx". Regardless of changing ByVal
to ByRef
, it's still giving the same result.
我有两个问题:1.这真的应该如何工作吗?2. 如果我的目标是将变量 passedPerson
的值保持为Max Weber",我做错了什么?
I have two questions:
1. Is this how it should really work?
2. What am I doing wrong if my aim is to keep the value of the variable passedPerson
to "Max Weber"?
推荐答案
对象变量不是对象:它是一种编程结构,我们用来保持对一个引用 - 实际对象不存在于我们的代码中,而是存在于 VBA 运行时上下文中.
An object variable isn't an object: it's a programming construct that we use to keep a reference to one - the actual object lives not in our code, but in the VBA runtime context.
Dim objRef1 As Object
Set objRef1 = New Collection
Debug.Print ObjPtr(objRef1)
Dim objRef2 As Object
Set objRef2 = objRef1
Debug.Print ObjPtr(objRef2)
这应该输出相同的地址,两次:两个变量都指向同一个对象:用一个更改该对象的属性...
This should output the same address, twice: both variables are pointing to the same object: changing the properties of that object with one...
objRef1.Add 42
...将影响另一个也指向的对象:
...will affect the very same object that the other also points to:
Debug.Print objRef2.Count ' prints 1 even though .Add was called against objRef1
<小时>
传递对象ByRef
(这是隐式默认值)意味着您将引用传递给对象指针,因此它可以简化为传递指针本身:ByRef
参数现在是其自身局部作用域中的局部变量,指向调用者也有引用的对象.
Passing an object ByRef
(it's the implicit default) means you're passing a reference to the object pointer, so it can be simplified to passing the pointer itself: the ByRef
parameter is now a local variable in its own local scope, pointing to an object to which the caller also has a reference.
Public Sub CallingCode()
Dim objRef1 As Object
Set objRef1 = New Collection
PassByReference objRef1
Debug.Print objRef1.Count ' error 91, the object reference is gone!
End Sub
Private Sub PassByReference(ByRef thing As Object)
thing.Add 42
Set thing = Nothing
End Sub
因为引用正在被传递,在任一过程中将其设置为Nothing
将使该对象的引用计数为0,并且该对象被销毁.这里对象被销毁,然后被访问,这会引发错误 91.
Because the reference is being passed, setting it to Nothing
in either procedure will bring that object's reference count to 0, and the object gets destroyed. Here the object is destroyed and then accessed, which raises error 91.
传递对象 ByVal
意味着您将引用的副本传递给对象指针 - 它是对同一个对象的不同引用强>:
Passing an object ByVal
means you're passing a copy of the reference to the object pointer - it's a distinct reference to the very same object:
Public Sub CallingCode()
Dim objRef1 As Object
Set objRef1 = New Collection
PassByValue objRef1
Debug.Print objRef1.Count ' 1
End Sub
Private Sub PassByValue(ByVal thing As Object)
thing.Add 42
Set thing = Nothing
End Sub
这里的本地副本被设置为Nothing
,但由于调用代码也有对该对象的引用,对象本身并没有被破坏——所以元素 42
被添加到集合中,Debug.Print
输出 1
.
Here the local copy is being set to Nothing
, but since the calling code also has a reference to that object, the object itself isn't getting destroyed - so the element 42
was added to the collection, and Debug.Print
outputs 1
.
这正是你的 PersonModel
发生的事情:传递它 ByVal
会给你一个对象指针的本地副本,指向与调用代码完全相同的对象- ByVal
不会深度克隆整个对象,它只是对同一个涉及的对象进行新的引用.因此,无论指向该对象的指针是按值传递还是按引用传递,修改该对象的属性都会影响完全相同的对象.
And that's exactly what's going on with your PersonModel
: passing it ByVal
gives you a local copy of the object pointer, pointing to the exact same object as the calling code - ByVal
doesn't deep-clone entire objects, it simply makes a new reference to the same involved object. Hence, modifying that object's properties affects the exact same object regardless of whether the pointer to that object is passed by value or by reference.
这篇关于作为参数传递的对象 ByVal 应该如何表现的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!