Results 1 to 10 of 10

Thread: Collection parameter ByRef vs ByVal

  1. #1

    Thread Starter
    Addicted Member
    Join Date
    Jul 2016
    Posts
    230

    Collection parameter ByRef vs ByVal

    Hey

    I'm wondering about the use of passing a collection ByRef, could anyone give a practical example?

    As I understand it, in the following example it makes no difference whether the r_col parameter is ByVal or ByRef:

    Code:
    Private Function getFooCol() As Collection
        Dim col As Collection
    
        Set col = New Collection
        Call appendFooToCol(col)
        Call appendBarToCol(col)
        'etc
    
        Set getFooCol = col
    End Function
    
    Private Sub appendFooToCol(ByRef r_col As Collection)
        Dim c As SomeClass
    
        Set c = New SomeClass
        c.foo = bar
    
        r_col.Add c, "key123"
    End Sub

  2. #2
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    9,942

    Re: Collection parameter ByRef vs ByVal

    It possibly makes a little difference. Objects (including Collections) work a bit differently than, say, a Long, when passing ByRef vs ByVal.

    When passing an object ByRef, you're passing the same variable pointing to the object. When passing ByVal, you get a new reference to the same object (not a new instantiation).

    So, what are the consequences?

    The primary one is this:
    • If an object is passed ByRef and then uninstantiated (or re-instantiated) in the call, upon return, the object variable will be uninstantiated (or re-instantiated).
    • On the other hand, an object passed ByVal will send a new reference to the called procedure. So, if that object variable gets uninstantiated (or re-instantiated), upon return, the original object variable will still point at the same (original) object.

    If you don't uninstantiate (or re-instantiate) the object in the call, then they're effectively the same.
    Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.

  3. #3
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    9,942

    Re: Collection parameter ByRef vs ByVal

    Here's a sample test/illustration:

    Code:
    
    Option Explicit
    
    Private Sub Form_Load()
        Dim c As Collection
        Set c = New Collection
        c.Add "asdf"
        c.Add "qwer"
        c.Add "zcxv"
    
        Debug.Print c.Count ' Prints 3
    
        Call ByValTest(c)
    
        Debug.Print c.Count ' Prints 3, our c variable declared here has the original object.
    
        Call ByRefTest(c)
    
        Debug.Print c.Count ' Prints 0, as our c variable was re-instantiated.
    
    End Sub
    
    
    Private Sub ByValTest(ByVal c As Collection)
        Set c = New Collection
    End Sub
    
    Private Sub ByRefTest(ByRef c As Collection)
        Set c = New Collection
    End Sub
    
    
    Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.

  4. #4

    Thread Starter
    Addicted Member
    Join Date
    Jul 2016
    Posts
    230

    Re: Collection parameter ByRef vs ByVal

    Thank you Elroy

  5. #5
    PowerPoster wqweto's Avatar
    Join Date
    May 2011
    Location
    Sofia, Bulgaria
    Posts
    5,169

    Re: Collection parameter ByRef vs ByVal

    Check out this sample code

    Code:
    Option Explicit
    
    Private m_oCol As Collection
    
    Private Sub Form_Load()
        Set m_oCol = New Collection
        appendFooToCol m_oCol
    End Sub
    
    Private Sub appendFooToCol(ByRef r_col As Collection)
        Dim c As SomeClass
    
        Set c = New SomeClass
    '    c.foo = bar
    
        DoSomethingElse
    
        r_col.Add c, "key123"
    End Sub
    
    Private Sub DoSomethingElse()
        Set m_oCol = Nothing
    End Sub
    In this case r_col.Add fails as the member m_oCol reference is set to Nothing and the ByRef r_col As Collection parameter is pointing to this m_oCol, not to the collection.

    To get a separate reference to the collection you have to use ByVal but this incurs a performance penalty (the compiler mints a brand new reference by calling extra AddRef/Release on each appendFooToCol invocation) so unless required by unfortunate circumstances (the DoSomethingElse code above) you should always prefer passing objects ByRef in your code like you already do.

    cheers,
    </wqw>

  6. #6

    Thread Starter
    Addicted Member
    Join Date
    Jul 2016
    Posts
    230

    Re: Collection parameter ByRef vs ByVal

    When passing an object ByRef, you're passing the same variable pointing to the object. When passing ByVal, you get a new reference to the same object (not a new instantiation).
    the ByRef r_col As Collection parameter is pointing to this m_oCol, not to the collection
    I was surprised to find that r_col.Add worked fine when r_col was passed ByVal. And here I've been coding in VB6 for some seven years.

    Code:
    Private m_oCol As Collection ' 1. Declare the object variable
    
    Private Sub Form_Load()
        Set m_oCol = New Collection ' 2. Instantiate the object variable. A reference is created which points to the memory address of the object.
        appendFooToCol m_oCol
    End Sub
    
    Private Sub appendFooToCol(ByVal r_col As Collection) ' 3. A second reference is created which points to the same object.
        Dim c As SomeClass
    
        Set c = New SomeClass
    '    c.foo = bar
    
        DoSomethingElse
    
        r_col.Add c, "key123" ' 5. The second reference is used.
    End Sub ' 6. The second pointer is now also destroyed, leaving nothing(?). All references are gone, so m_oCol is destroyed
    
    Private Sub DoSomethingElse()
        Set m_oCol = Nothing ' 4. The first reference is destroyed, but the object it points to is left untouched.
    End Sub
    Are my comments correct?

    Is "reference" a pointer? I see that it could be more complicated than that: https://www.vbforums.com/showthread....classes-in-VB6

    Is there any practical use of passing a collection ByVal?

    P.S. How do you make the code formatted in color?

  7. #7
    PowerPoster wqweto's Avatar
    Join Date
    May 2011
    Location
    Sofia, Bulgaria
    Posts
    5,169

    Re: Collection parameter ByRef vs ByVal

    > Is "reference" a pointer?

    Yes.

    The point is that r_col identifier in appendFooToCol body is also a parameter which can be a ByVal reference or a ByRef reference. In C/C++ parlance ByVal is a normal pointer ICollection *r_col while the second one is a double pointer ICollection **r_col i.e. a pointer to a reference.

    The difference is profound and the compiler generates different code to "dereference" such parameters. For instance your r_col.Add method call will generate different ASM code for ByVal vs ByRef parameters.

    > Is there any practical use of passing a collection ByVal?

    Yes, there are certain use-cases where you'd want to "lock" the instance throughout the method body while sacrificing a bit of performance at the callsite, usually when you don't expect this method to be called in tight loop or similar.

    cheers,
    </wqw>

  8. #8
    PowerPoster
    Join Date
    Feb 2006
    Posts
    24,482

    Re: Collection parameter ByRef vs ByVal

    There are other factors to consider when cross-process and cross-machine COM/DCOM marshaling is involved. ByRef calls can lead to data copying in both directions instead of just one. Also some objects have special "smarts" internally (ADODB.Recordset for one) to improve marshaling efficiency.

    ByRef implies updating, and using it willy-nilly can lead to hard to diagnose bugs when the called routine alters the original passed in.

    This is a pretty scary fundamental thing to ask questions about. It's almost scarier to see it "explained" in terns of what C or an assembler does. If you are struggling with a concept like this in Basic the odds are that your notions about pointers are far more tenuous.


    You should normally pass everything ByVal unless you have some specific reason not to. Examples might be avoiding the copying of very long String data or performing some other more desperate micro-optimization. Better examples include cases where the function is intended to update the data passed in.

  9. #9
    PowerPoster wqweto's Avatar
    Join Date
    May 2011
    Location
    Sofia, Bulgaria
    Posts
    5,169

    Re: Collection parameter ByRef vs ByVal

    Quote Originally Posted by dilettante View Post
    There are other factors to consider when cross-process and cross-machine COM/DCOM marshaling is involved. ByRef calls can lead to data copying in both directions instead of just one.
    No, this is not true for reference parameters. Cargo-culting and expert advice here has brought enough confusion in these forums. There is no "data copying" with references unless a custom marshaller is involved.

    Quote Originally Posted by dilettante View Post
    Also some objects have special "smarts" internally (ADODB.Recordset for one) to improve marshaling efficiency.
    Yes, only for client-side (disconnected) recordsets. The marshaller has *no* information if the parameter is passed ByRef or ByVal i.e. it works the same way in both cases. It does *not* marshal any of the changes to the data back to the caller process i.e. original recordset remains unmodified. There is no controlling "data copying in both directions instead of just one" using ByVal vs ByRef even with custom marshallers.

    Quote Originally Posted by dilettante View Post
    ByRef implies updating, and using it willy-nilly can lead to hard to diagnose bugs when the called routine alters the original passed in.
    No, you can update object properties at will. There is no more danger in doing this than if the reference was passed ByVal.

    Quote Originally Posted by dilettante View Post
    This is a pretty scary fundamental thing to ask questions about. It's almost scarier to see it "explained" in terns of what C or an assembler does. If you are struggling with a concept like this in Basic the odds are that your notions about pointers are far more tenuous.
    Could be scary for some but at least this explains the observed reality. I intend to keep explaining low-level stuff with C/C++, IDL and ASM in every BASIC forum I frequent.

    Quote Originally Posted by dilettante View Post
    You should normally pass everything ByVal unless you have some specific reason not to. Examples might be avoiding the copying of very long String data or performing some other more desperate micro-optimization. Better examples include cases where the function is intended to update the data passed in.
    The OP is about references. My advice is to keep using the default ByRef passing mechanism with reference parameters unless there is a good reason to switch to ByVal.

    cheers,
    </wqw>

  10. #10

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width