-
Sep 27th, 2022, 10:49 AM
#1
Thread Starter
Addicted Member
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
-
Sep 27th, 2022, 10:59 AM
#2
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.
-
Sep 27th, 2022, 11:06 AM
#3
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.
-
Sep 28th, 2022, 04:07 AM
#4
Thread Starter
Addicted Member
Re: Collection parameter ByRef vs ByVal
-
Sep 28th, 2022, 04:34 AM
#5
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>
Last edited by wqweto; Sep 28th, 2022 at 05:20 AM.
-
Sep 28th, 2022, 07:36 AM
#6
Thread Starter
Addicted Member
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?
-
Sep 28th, 2022, 07:49 AM
#7
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>
-
Sep 28th, 2022, 11:30 AM
#8
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.
-
Sep 28th, 2022, 02:00 PM
#9
Re: Collection parameter ByRef vs ByVal
Originally Posted by dilettante
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.
Originally Posted by dilettante
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.
Originally Posted by dilettante
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.
Originally Posted by dilettante
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.
Originally Posted by dilettante
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>
-
Sep 28th, 2022, 02:48 PM
#10
Re: Collection parameter ByRef vs ByVal
A type-lib marshaler uses [in] [out] attributes to determine if a parameter should be passed from/to a COM server.
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|