Results 1 to 25 of 25

Thread: Good Programming Practice: About "ByVal MyObject"

  1. #1

    Thread Starter
    Frenzied Member
    Join Date
    Aug 2020
    Posts
    1,421

    Good Programming Practice: About "ByVal MyObject"

    I know the difference between "ByRef MyObject" and "ByVal MyObject". But I have a bad programming habit, that is, for primitive data types, I'm accustomed to use ByVal, and for reference data types, I'm accustomed to use ByRef. E.g:

    Code:
    Function MyFunc(ByVal Token As Long, MyObj As MyClass) As Boolean        '--- At this time, MyObj defaults to ByRef ---
    
    End Function
    
    Property Set MyProp(ByVal Value As Long)
    
    End Property
    
    Property Set MyProp2(Value As MyClass)        '--- At this time, Value defaults to ByRef ---
    
    End Property
    In the vast majority of cases, this programming habit of mine will not be a problem. Right now, I'm developing my own base- class-lib. I'm thinking about a question: Should I use a more formalized way of programming? E.g:

    Code:
    Function MyFunc(ByVal Token As Long, ByVal MyObj As MyClass) As Boolean
    
    End Function
    
    Property Set MyProp(ByVal Value As Long)
    
    End Property
    
    Property Set MyProp2(ByVal Value As MyClass)
    
    End Property
    Using a more formalized way of programming means that I need to make a lot of changes to my original code. I don't know if this is worth it?

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

    Re: Good Programming Practice: About "ByVal MyObject"

    ByRef vs. ByVal is more about the cost of marshaling over DCOM. However it also applies locally in terms of the cost of data copying and protecting the calling client data.

    Locally or remotely passing an object ByVal is relatively low cost and best practice. The exception is when the call is meant to alter the object reference itself to point to a different instance of the class.

    One could argue that ByRef is cheaper locally since thee is no need to create and count a separate reference. Remotely of course, it means the call has the overhead of round-tripping the state of the object. But "remote" also means cross-process as well as cross-machine.

    There is no COM inheritance, so I'm not sure what you mean by base class here.

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

    Re: Good Programming Practice: About "ByVal MyObject"

    ByVal vs ByRef for reference types (As Object) has a completely different semantics than ByVal vs ByRef for value types (Long, Double, *String*, UDT, etc.) For reference types (a.k.a. pointers) the modifier affects the way the *pointer* is transfered, not the whole objects, has nothing to do with cloning/copying the object. In COM it's not possible to designate you want an *object* to be cloned on method call so ByVal works by "cloning" the pointer i.e. if you modify the pointer inside the function being called it's not propagated upstream.

    With ByRef on reference types the compiler emits double pointers i.e. void **ppUnk which is a pointer to a pointer to an object. This double pointer can be used to modify the pointer to the object i.e. the called function can modify the reference in the caller function.

    One should *always* prefer using ByRef with object types because this prevents the compiler from emitting an AddRef/Release pair of calls on the actual object argument which is the case with ByVal modifier. Once again, if you have an function with ByVal MyObject As Object parameter each time you call this function an AddRef is executed on the actual object which is passed in and then on exit Release is called on the same object. This is an overhead which might amass to a significant portion if calling the function in a loop and the function does not do anything remotely computationally intensive (e.g. only checking if reference passed Is Nothing or similar nonsense).

    Now, there *are* coclasses with custom marshallers like ADO Recordset for instance. Under COM+/DCOM (cross-process or cross-machine only, not inter-thread) this marshaller kicks in and serializes/deserializes the object on each method call no matter the modifier i.e. a *ByRef* rs As Recordset will be cloned/copied across the process/machine effectively implementing "by value" semantics but still in this case whether the parameter is ByRef or ByVal does not matter, it's the marshaller which decides the semantics. For instance with ByVal rs As Recordset the changes performed in the called process might be back-propagated on return to caller process too or might be dropped in ByRef rs As Recordset case -- I think it's the latter with ADO Recordset.

    cheers,
    </wqw>

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

    Re: Good Programming Practice: About "ByVal MyObject"

    In my mind, for objects, it's quite simple. Passing an object ByRef uses the same object reference, whereas passing an object ByVal creates a new reference to the object.

    (I know that's not 100% true, but it's true enough to help remember how things work "functionally", without worrying about all the "under the hood" hoops that are jumped through.)

    So, the result of this is, if passed ByRef and, within the call, we uninstantiate the object (Set Obj = Nothing) then, when we return, the passed object variable is also uninstantiated. If passed ByVal, the original object variable is unaffected by an uninstantiation.

    And further ramifications take place if we, say, completely reinstantiate our passed object (again, ByVal not affecting the original object and instead creates a separate object, whereas ByRef changes the original object argument).

    I've also found that passing objects ByVal isn't 100% bulletproof, especially with tree items. And I've gotten to where I seldom use it.
    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.

  5. #5
    Addicted Member
    Join Date
    Oct 2011
    Posts
    179

    Re: Good Programming Practice: About "ByVal MyObject"

    Quote Originally Posted by Elroy View Post
    In my mind, for objects, it's quite simple. Passing an object ByRef uses the same object reference, whereas passing an object ByVal creates a new reference to the object.

    (I know that's not 100% true
    Why do you say that it is not 100% true?

    Code:
    Private Sub Form_Load()
        Dim c As MyClass
        
        Set c = New MyClass
        
        MyProc c
    End Sub
    
    Private Sub MyProc(ByVal Obj As MyClass)
        
    End Sub
    I think it is equivalent to:

    Code:
    Private Sub Form_Load()
        Dim c As MyClass
        
        Set c = New MyClass
        
        MyProc c
    End Sub
    
    Private Sub MyProc(ByRef ObjRef As MyClass)
        Dim Obj As MyClass
        
        Set Obj = ObjRef
        
    End Sub
    You can set Obj to Nothing or to another object instance without any consequence to the caller.

    Quote Originally Posted by Elroy View Post
    I've also found that passing objects ByVal isn't 100% bulletproof, especially with tree items. And I've gotten to where I seldom use it.
    The same: that looks strange.

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

    Re: Good Programming Practice: About "ByVal MyObject"

    Quote Originally Posted by argen View Post
    Quote Originally Posted by Elroy View Post
    In my mind, for objects, it's quite simple. Passing an object ByRef uses the same object reference, whereas passing an object ByVal creates a new reference to the object.

    (I know that's not 100% true
    Why do you say that it is not 100% true?
    Well, in the past, I've past object variables as ByRef and then watched the reference count, and it goes up. Even when passing ByRef, additional references are created. It's just "made to look like they're not under-the-hood". That's why I said that.

    Quote Originally Posted by argen View Post
    Quote Originally Posted by Elroy View Post
    I've also found that passing objects ByVal isn't 100% bulletproof, especially with tree items. And I've gotten to where I seldom use it.
    The same: that looks strange.
    This one may have been more about the strange behavior of tree Nodes than the ByVal/ByRef specification of object passing. The only time I saw some unexpected behavior was when I was passing tree Nodes around, and those things are very strange.
    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.

  7. #7
    Addicted Member
    Join Date
    Oct 2011
    Posts
    179

    Re: Good Programming Practice: About "ByVal MyObject"

    Quote Originally Posted by Elroy View Post
    Well, in the past, I've past object variables as ByRef and then watched the reference count, and it goes up. Even when passing ByRef, additional references are created. It's just "made to look like they're not under-the-hood". That's why I said that.
    Of course, the receiving procedure can make all the new references that it wants, no matter how the "first" reference were passed.

    But that is not in control (or in scope) of ByVal/ByRef

    It is like saying that you pass a string ByRef but the receiving procedure made new copies anyway.

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

    Re: Good Programming Practice: About "ByVal MyObject"

    Quote Originally Posted by argen View Post
    It is like saying that you pass a string ByRef but the receiving procedure made new copies anyway.
    That's pretty much what's happening, and in fact, that's precisely what happens when we pass fixed-length-strings. And those same ByVal/ByRef rules apply.

    However, with an object, all we ever pass is a pointer to the object (i.e., what we get from ObjPtr). It's just a matter of whether we actually pass that pointer (ByVal) or a pointer to that pointer (ByRef). However, as I've tried to say, it's not quite that simple because VB6 works hard to keep the reference count accurate. So, regardless of whether you pass an object as ByRef or ByVal, if a new object variable is created that references that object (such as a receiving procedure), it's going to increment the object reference count. Again, this is all "under the hood" stuff.

    ADDED: And, I've always felt like there was this added complexity because we can pass module-level or global-level variables as arguments. The only way VB6 can keep everything straight is to create more references (for both ByVal and ByRef).
    Last edited by Elroy; Jun 24th, 2022 at 12:17 PM.
    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.

  9. #9
    Addicted Member
    Join Date
    Oct 2011
    Posts
    179

    Re: Good Programming Practice: About "ByVal MyObject"

    Quote Originally Posted by Elroy View Post
    That's pretty much what's happening, and in fact, that's precisely what happens when we pass fixed-length-strings. And those same ByVal/ByRef rules apply.

    However, with an object, all we ever pass is a pointer to the object (i.e., what we get from ObjPtr). It's just a matter of whether we actually pass that pointer (ByVal) or a pointer to that pointer (ByRef). However, as I've tried to say, it's not quite that simple because VB6 works hard to keep the reference count accurate. So, regardless of whether you pass an object as ByRef or ByVal, if a new object variable is created that references that object (such as a receiving procedure), it's going to increment the object reference count. Again, this is all "under the hood" stuff.
    But Elroy, the point is that the receiving procedure can add as many new references as it wants (make new (counted)pointers).
    ByRef does not guarantee that that won't happen, as it does not guarantee that a string passed ByRef won't be copied into new variables.
    I think it is pretty much the same.

    The only difference with objects (regarding ByVal/ByRef ) is that we are talking about references, not about the objects themselves. We are not talking about cloning objects (as wqweto already explained).

  10. #10

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

    Re: Good Programming Practice: About "ByVal MyObject"

    Quote Originally Posted by argen View Post
    The only difference with objects (regarding ByVal/ByRef ) is that we are talking about references, not about the objects themselves. We are not talking about cloning objects (as wqweto already explained).
    I understand that! Instantiating new objects is a completely separate discussion (other than when an object variable down in a called procedure is re-instantiated). Argen, I'm good to go on this one, and I think maybe SearchingDataOnly is as well. I'm out'a this thread.
    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.

  12. #12
    PowerPoster yereverluvinuncleber's Avatar
    Join Date
    Feb 2014
    Location
    Norfolk UK (inbred)
    Posts
    2,235

    Re: Good Programming Practice: About "ByVal MyObject"

    Good informative thread
    https://github.com/yereverluvinunclebert

    Skillset: VMS,DOS,Windows Sysadmin from 1985, fault-tolerance, VaxCluster, Alpha,Sparc. DCL,QB,VBDOS- VB6,.NET, PHP,NODE.JS, Graphic Design, Project Manager, CMS, Quad Electronics. classic cars & m'bikes. Artist in water & oils. Historian.

    By the power invested in me, all the threads I start are battle free zones - no arguing about the benefits of VB6 over .NET here please. Happiness must reign.

  13. #13
    Addicted Member
    Join Date
    Oct 2011
    Posts
    179

    Re: Good Programming Practice: About "ByVal MyObject"

    Quote Originally Posted by Elroy View Post
    I understand that! Instantiating new objects is a completely separate discussion (other than when an object variable down in a called procedure is re-instantiated). Argen, I'm good to go on this one, and I think maybe SearchingDataOnly is as well. I'm out'a this thread.
    That's was not the point, that was a complementary comment, just in case that clarification was needed.

    The point was that you said:

    Quote Originally Posted by Elroy View Post
    In my mind, for objects, it's quite simple. Passing an object ByRef uses the same object reference, whereas passing an object ByVal creates a new reference to the object.

    (I know that's not 100% true
    And I asked why you said that, because the first statement is actually 100% true.
    I asked because I thought you had some other information, but I see you don't.

    I'm still not sure why you said that.
    It looks like something that doesn't make sense now.
    What part of that 100% is not true, that was what I was asking.

  14. #14
    Fanatic Member
    Join Date
    Jun 2019
    Posts
    557

    Re: Good Programming Practice: About "ByVal MyObject"

    We had some rules when defining parameters to avoid strange "effects" when multiple developers work on same (VB6) code base:

    • Always specify ByVal or ByRef and don't leave it to default as devs were coming from other languages where parameters are passed by default as values.
    • Always specify ByVal and ByRef in VB.NET (which defaults to ByVal for parameters if not explicitly specified) when code (sub/function definition) is copy/pasted to VB6 (and vice versa).
    • Always use ByVal except the cases where variable should be returned as The Trick described above.
    • Add comments for input and output parameters for each sub or function.
    • Always document (as comments) when ByRef parameter is modified and why.
    • Take special care when merging sub/function parameter changes into main branch. Sometimes devs forgot to add ByVal/ByRef, other times they just don't know what they are doing.
    • When parameters are too much to fit into single line - split to multiple lines for readability.

    Sticking to rules helps avoiding or at least allow early detection of problematic code.

    There are other methods for earlier detection of misbehaving code. Already mentioned code review before merging. Another is writing tests, but they are not automated in VB6 and need to add manually to the build steps. Tests can be written to detect if routine wrongly affects reference parameter and fail so the developer will know that something wrong happened.

  15. #15
    Addicted Member
    Join Date
    Oct 2011
    Posts
    179

    Re: Good Programming Practice: About "ByVal MyObject"

    Quote Originally Posted by peterst View Post
    [*]Always use ByVal except the cases where variable should be returned as The Trick described above.
    There is a performance penalty copying strings when is not necessary.
    I prefer to pass strings ByRef when they are not going to be changed.

    And I'm not sure if the same does not apply to type Double, that has 8 bits (I think a reference might be faster).

  16. #16
    Fanatic Member
    Join Date
    Jun 2019
    Posts
    557

    Re: Good Programming Practice: About "ByVal MyObject"

    Quote Originally Posted by argen View Post
    There is a performance penalty copying strings when is not necessary.
    When working with other 10+ devs - some experienced but most not so much, rules are rules. Performance penalty cannot compare to wasted weeks and months debugging strange issues and referenced parameters are just another source for that.

    When something becomes really slow - then optimize for performance and document _why_ it is done.

  17. #17
    Addicted Member
    Join Date
    Oct 2011
    Posts
    179

    Re: Good Programming Practice: About "ByVal MyObject"

    Quote Originally Posted by peterst View Post
    When working with other 10+ devs - some experienced but most not so much, rules are rules. Performance penalty cannot compare to wasted weeks and months debugging strange issues and referenced parameters are just another source for that.

    When something becomes really slow - then optimize for performance and document _why_ it is done.
    Yes, that makes sense.

  18. #18
    Fanatic Member
    Join Date
    Jun 2019
    Posts
    557

    Re: Good Programming Practice: About "ByVal MyObject"

    Probably someone already made real world benchmarks on ByVal vs ByRef but I just finished testing of old tool that processes millions of records with mostly string data - all hold in memory for performance. Actually the original project even has no advanced optimizations turned on.

    So I ran 3 versions: original, advanced optimizations on, advanced optimizations and all params are passed by ref. The code was written to be safe (I am the only maintainer so no one else touched it) when managing parameters so there is no problem with ByRef.

    All versions were benchmarked 3 times. The run time was from 8 min 33 seconds to 8 min 38 seconds for all 9 runs. This is total processing time without the I/O which may affect the benchmark. Interestingly for me is that the fastest run was from the original version.

    If there was so big impact when passing by value or by reference, I expected to have at least a minute difference. But... there isn't.

    The tested routine is part of bigger set of processing routines that take about 3 hours and it is very annoying to wait but it is used once or twice per year.

    /off
    In past I've tested to move the code to different platform (again visual, again basic) and first (~8 minutes runtime) + second routine (~40-45 minutes runtime) just ran for shy 35 to 55 seconds (including load data from sql server after cold restart) by abusing large amount of memory (which was obviously not available for 32 bit process).

  19. #19
    Addicted Member
    Join Date
    Oct 2011
    Posts
    179

    Re: Good Programming Practice: About "ByVal MyObject"

    All depends on the other things that the program does.
    The impact can be negligible in many cases, because the time spent in copying strings is not much usually (related to the time spent in other things).

    But in a function that deals mainly with strings and is a core function, for example a StrRev replacement, there it is more important to optimize.

  20. #20
    Fanatic Member
    Join Date
    Jun 2019
    Posts
    557

    Re: Good Programming Practice: About "ByVal MyObject"

    Optimization should be done by library authors. Same with that Len(B) discussion - all this is important for library authors. Developers will use the library as abstraction layer that hides the complexity and with optimized performance. It is always better to focus on the real problem that should be solved instead of managing some common tasks and wondering which Win32API call to use.

    It is strange to me how many people here refuse to use great and optimized library like vbRichClient... Actually I've seen same in the neighbor subforum where people never heard or just ignore NuGet...

  21. #21
    Addicted Member
    Join Date
    Oct 2011
    Posts
    179

    Re: Good Programming Practice: About "ByVal MyObject"

    Everything depends on the nature of the program. For most programs optimization isn't important, because they are not time consuming processes, but not for all.

    Now it is divided into frontend and backend. I would say that optimization is more important for backend developers and no so much for frontend developers.
    Library authors, no doubt it is important (in most cases, not for all either).

    Quote Originally Posted by peterst View Post
    It is strange to me how many people here refuse to use great and optimized library like vbRichClient...
    Do you use it?
    Last edited by argen; Jun 24th, 2022 at 05:47 PM.

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

    Re: Good Programming Practice: About "ByVal MyObject"

    Quote Originally Posted by peterst View Post
    I've seen same in the neighbor subforum where people never heard or just ignore NuGet...
    Now that would be hilarious. . . Really?!

    cheers,
    </wqw>

  23. #23

    Thread Starter
    Frenzied Member
    Join Date
    Aug 2020
    Posts
    1,421

    Re: Good Programming Practice: About "ByVal MyObject"

    Sorry for the late reply. Thanks to everyone involved in this thread. Thanks dilettante, wqweto, Elroy, argen, The trick, yereverluvinuncleber, peterst.

    After taking everyone's opinions together, I decided to take the following solution:

    For all object variables, use ByRef (that is, still keep my original programming habits), but will make some optimizations, such as:

    (1) For object variables that need to be returned, use "Explicit-ByRef", and the object variable names are prefixed with Out_ or Return_.

    (2) For object variables that do not need to be returned, use the "Implicit-ByRef", that is, omit the ByRef keyword.

    (3) The rules for string parameters are also the same as those for object parameters.
    Last edited by SearchingDataOnly; Jun 26th, 2022 at 08:16 AM.

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

    Re: Good Programming Practice: About "ByVal MyObject"

    Quote Originally Posted by SearchingDataOnly View Post
    Sorry for the late reply. Thanks to everyone involved in this thread. Thanks dilettante, wqweto, Elroy, argen, The trick, yereverluvinuncleber, peterst.

    After taking everyone's opinions together, I decided to take the following solution:

    For all object variables, use ByRef (that is, still keep my original programming habits), but will make some optimizations, such as:

    (1) For object variables that need to be returned, use "Explicit-ByRef", and the object variable names are prefixed with Out_ or Return_.

    (2) For object variables that do not need to be returned, use the "Implicit-ByRef", that is, omit the ByRef keyword.

    (3) The rules for string parameters are also the same as those for object parameters.
    Sounds like a good set of rules to me. I like the idea of explicit ByRef on those objects that are changed by the called procedure. :thumbsup:
    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.

  25. #25
    Hyperactive Member
    Join Date
    Jan 2018
    Posts
    264

    Re: Good Programming Practice: About "ByVal MyObject"

    Strictly for documentation purposes, I think Object ByRef makes a lot of sense considering the average skill set of VB(A) programmers. You're indicating that the object being passed can be modified, even if internally the pointer will be untouched 99% of the time.

    edit: Oops, I missed the last couple posts. SearchData's decision makes a lot of sense to me from a documentation perspective. The Return_ or Output_ type would presumably only be for the rarer situations where the object pointer is modified, so it shouldn't be hurting readability.
    Last edited by ahenry; Jun 27th, 2022 at 08:44 AM.

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