作为参数传递的对象 ByVal 应该如何表现
How object passed as an argument ByVal should behave
我是 studying/learning ByVal 和 ByRef 在处理调用对象时的行为。所以我创建了这个 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
我制作了一个标准模块,试图查看如果对象在子例程中作为 ByVal 或 ByRef 传递,其值会受到怎样的影响。这是标准模块中的代码
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.Print passedPerson.fullName
会给我一个不变的值 "Max Weber"。但相反,它给出了新值 "Karl Marx"。即使我将过程 passByVal 的代码更改为:
Private Sub passByVal(ByVal person As PersonModel)
Dim newPerson As PersonModel
Set newPerson = New PersonModel
Set newPerson = person
Set passedPerson = newPerson
End Sub
代码的第二次调用部分 Debug.Print passedPerson.fullName
仍然给出 "Karl Marx"。无论将 ByVal
更改为 ByRef
,它仍然给出相同的结果。
我有两个问题:
1. 这真的应该是这样吗?
2. 如果我的目标是将变量 passedPerson
的值保持为 "Max Weber",我做错了什么?
对象变量不是对象:它是一种编程结构,我们用它来保持对某个对象的 引用 - 实际对象不存在于我们的代码中,而是存在于VBA 运行时上下文。
Dim objRef1 As Object
Set objRef1 = New Collection
Debug.Print ObjPtr(objRef1)
Dim objRef2 As Object
Set objRef2 = objRef1
Debug.Print ObjPtr(objRef2)
这应该两次输出相同的地址:两个变量都指向同一个对象:用一个更改该对象的属性...
objRef1.Add 42
...将影响另一个对象也指向的同一对象:
Debug.Print objRef2.Count ' prints 1 even though .Add was called against objRef1
传递对象 ByRef
(这是隐式默认值)意味着您将 reference 传递给对象指针,因此它可以简化为 传递指针本身:ByRef
参数现在是其自身局部范围内的局部变量,指向调用者也有引用的对象。
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。
传递对象 ByVal
意味着您将 引用 的副本传递给对象指针 - 它是对对象指针的独特引用 同一个对象:
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
.
这正是您的 PersonModel
所发生的事情:传递它 ByVal
为您提供对象指针的本地副本,指向与调用代码完全相同的对象 - ByVal
不会深度克隆整个对象,它只是对同一涉及的对象进行新引用。因此,无论指向该对象的指针是按值还是按引用传递,修改该对象的属性都会影响完全相同的对象。
我是 studying/learning ByVal 和 ByRef 在处理调用对象时的行为。所以我创建了这个 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
我制作了一个标准模块,试图查看如果对象在子例程中作为 ByVal 或 ByRef 传递,其值会受到怎样的影响。这是标准模块中的代码
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.Print passedPerson.fullName
会给我一个不变的值 "Max Weber"。但相反,它给出了新值 "Karl Marx"。即使我将过程 passByVal 的代码更改为:
Private Sub passByVal(ByVal person As PersonModel)
Dim newPerson As PersonModel
Set newPerson = New PersonModel
Set newPerson = person
Set passedPerson = newPerson
End Sub
代码的第二次调用部分 Debug.Print passedPerson.fullName
仍然给出 "Karl Marx"。无论将 ByVal
更改为 ByRef
,它仍然给出相同的结果。
我有两个问题:
1. 这真的应该是这样吗?
2. 如果我的目标是将变量 passedPerson
的值保持为 "Max Weber",我做错了什么?
对象变量不是对象:它是一种编程结构,我们用它来保持对某个对象的 引用 - 实际对象不存在于我们的代码中,而是存在于VBA 运行时上下文。
Dim objRef1 As Object
Set objRef1 = New Collection
Debug.Print ObjPtr(objRef1)
Dim objRef2 As Object
Set objRef2 = objRef1
Debug.Print ObjPtr(objRef2)
这应该两次输出相同的地址:两个变量都指向同一个对象:用一个更改该对象的属性...
objRef1.Add 42
...将影响另一个对象也指向的同一对象:
Debug.Print objRef2.Count ' prints 1 even though .Add was called against objRef1
传递对象 ByRef
(这是隐式默认值)意味着您将 reference 传递给对象指针,因此它可以简化为 传递指针本身:ByRef
参数现在是其自身局部范围内的局部变量,指向调用者也有引用的对象。
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。
传递对象 ByVal
意味着您将 引用 的副本传递给对象指针 - 它是对对象指针的独特引用 同一个对象:
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
.
这正是您的 PersonModel
所发生的事情:传递它 ByVal
为您提供对象指针的本地副本,指向与调用代码完全相同的对象 - ByVal
不会深度克隆整个对象,它只是对同一涉及的对象进行新引用。因此,无论指向该对象的指针是按值还是按引用传递,修改该对象的属性都会影响完全相同的对象。