Объект коллекции - ByRef - ByVal

Я использую VBA в Access 2013.

В обычном модуле есть 2 процедуры, RunProc() и PopulateCollection()

Когда выполняется RunProc, он вызывает PopulateCollection, где переданным аргументом является экземпляр Collection с именем MyCol.

PopulateCollection добавляет 3 элемента, а затем RunProc продолжает итерацию коллекции.

Мой вопрос/проблема заключается в следующем:

Я хочу, чтобы аргумент MyCol в RunProc НЕ заполнялся PopulateCollection. Каков правильный способ сделать это?

Почему PopulateCollection заполняет и аргумент, и параметр?

' --------Module1------------------
Option Compare Database
Option Explicit

Dim i As Integer
Dim MyCol As VBA.Collection

Sub RunProc()
    Set MyCol = New VBA.Collection
    
    PopulateCollection MyCol
    
    For i = 1 To MyCol.Count
        Debug.Print MyCol.Item(i)
    Next i
End Sub

Function PopulateCollection(ByRef pMyCol As VBA.Collection)
    For i = 1 To 3
       pMyCol.Add "Item" & i
    Next i            
End Function

Вот еще один способ задать мой вопрос:

Option Compare Database
Option Explicit

Sub Proc1()

    Dim myInt As Integer
    myInt = 1
    
    Proc2 myInt
    
    Debug.Print myInt
    
    myInt = 1
    
    Proc3 myInt
    
    Debug.Print myInt
        
End Sub

Sub Proc2(ByVal pmyInt)

    pmyInt = pmyInt + 1
    Debug.Print pmyInt
    
End Sub

Sub Proc3(ByRef pmyInt)

    pmyInt = pmyInt + 1
    Debug.Print pmyInt
    
End Sub

'Consider the 3 procedures: Proc1, Proc2, Proc3

'Proc1 calls Proc2 and Proc3

'The only difference between Proc2 and Proc3 is that
'the parameter pmyInt is called differently: ByVal vs ByRef

'Proc2 does not change the argument myInt
'Proc3 does change the argument myInt

'The root of my question is why the same behavior is
'not exhibited with an Object (VBA.Collection)
'Assuming I wanted to not have the original Collection altered
'how would I proceed?

person Michael    schedule 17.02.2014    source источник
comment
Что вы подразумеваете под и аргументом, и параметром? Ваш вопрос немного неясен, можете ли вы попытаться более четко выразить то, что вы ожидаете? (Кроме того, если вы не хотите PopulateCollection() изменять свою коллекцию, зачем вообще вызывать эту функцию?)   -  person Tomalak    schedule 17.02.2014
comment
Ваш вопрос просто не имеет смысла... если вы не хотите, чтобы myCol заполнялось, не передавайте его функции.   -  person    schedule 17.02.2014
comment
Когда RunProc выполняется и в конце процедуры он зацикливает MyCol, он ничего не должен возвращать, потому что MyCol (аргумент) не должен быть заполнен. Только pMyCol должен быть заполнен в PopulateCollection, а не MyCol   -  person Michael    schedule 17.02.2014
comment
Другими словами, создайте экземпляр коллекции, а затем передайте ее процедуре. Эта процедура берет коллекцию и что-то с ней делает. Но я не хочу, чтобы исходная коллекция: MyCol была изменена только переданная коллекция: pMyCol   -  person Michael    schedule 17.02.2014
comment
Но функция называется PopulateCollection, не стоит удивляться, когда она делает то, что написано на коробке. pMyCol и MyCol одно и то же, они не разделены. Возникает вопрос: если вы не хотите заполнять коллекцию, почему вы вызываете функцию PopulateCollection?   -  person Tomalak    schedule 17.02.2014
comment
Итак, какой смысл передавать исходный MyCol в функцию? Почему бы вам просто не преобразовать вашу функцию в процедуру Sub и не создать новую коллекцию внутри ее тела? Опять же, какая связь между MyCol и pMyCol?   -  person    schedule 17.02.2014
comment
Спасибо за помощь. Вот еще один способ задать мой вопрос:   -  person Michael    schedule 17.02.2014


Ответы (1)


В VBA объекты (например, коллекции) всегда передаются по ссылке. Когда вы передаете объект ByRef, передается адрес объекта, и PopulateCollection может изменить ссылку.

Когда вы передаете его ByVal, передается копия ссылки. Копия ссылки по-прежнему указывает на исходную коллекцию, но если вы измените копию, вы не измените ссылку в RunProc.

Sub RunProc()

    Dim MyCol As Collection
    Dim i As Long

    Set MyCol = New Collection

    PopCollByVal MyCol

    'I changed what pMyCol points to but *after*
    'I populated it when it still pointed to MyCol
    'so this returns 3
    Debug.Print "ByVal: " & MyCol.Count

    PopCollByRef MyCol

    'When I changed the reference pMyCol it changed
    'MyCol so both became a new Collection. This
    'return 0
    Debug.Print "ByRef: " & MyCol.Count

End Sub

Function PopCollByVal(ByVal pMyCol As Collection)

    Dim i As Long

    'The pointer pMyCol is a copy of the reference
    'to MyCol, but that copy still points to MyCol
    For i = 1 To 3
        'I'm changing the object that is pointed to
        'by both MyCol and pMyCol
        pMyCol.Add "Item" & i
    Next i

    'I can change what pMyCol points to, but I've
    'already populated MyCol because that's what
    'pMyCol pointed to when I populated it.
    Set pMyCol = New Collection

End Function
Function PopCollByRef(ByRef pMyCol As Collection)

    Dim i As Long

    'The pointer pMyCol is the actual reference
    'to MyCol
    For i = 1 To 3
        pMyCol.Add "Item" & i
    Next i

    'When I change what pMyCol points to, I also
    'change what MyCol points to because I passed
    'the pointer ByRef. Now MyCol is a new collection
    Set pMyCol = New Collection

End Function
person Dick Kusleika    schedule 18.02.2014