作为参数ByVal传递的对象应如何表现

时间:2019-03-28 18:21:30

标签: vba pass-by-reference pass-by-value

我正在学习/学习ByVal和ByRef的行为,涉及到调用对象。所以我创建了这个类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传递时如何影响该对象的值。这是标准模块中的代码

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”值。但取而代之的是,它赋予了新的价值“卡尔·马克思”。即使我将过程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”,那我在做什么错了?

1 个答案:

答案 0 :(得分:3)

对象变量不是对象:它是一种编程构造,我们使用它来保持对 reference 的引用-实际对象不在我们的代码中,而是在VBA运行时上下文中。 / p>

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 传递给对象指针,因此可以简化为传递指针本身< / em>: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

此处将 local copy 设置为Nothing,但是由于调用代码也具有对该对象的引用,因此该对象本身不会被破坏-因此元素{ {1}}已添加到集合中,并且42输出Debug.Print

这就是1的全部操作:传递PersonModel给您一个对象指针的本地副本,指向与调用代码完全相同的对象-ByVal不会深度克隆整个对象,它只是对相同的所涉及对象进行了新引用。因此,修改该对象的属性会影响完全相同的对象,而不管指向该对象的指针是通过值传递还是通过引用传递。