Vb.NET: ByVal vs ByRef

Collapse
X
 
  • Time
  • Show
Clear All
new posts
  • Frinavale
    Recognized Expert Expert
    • Oct 2006
    • 9749

    Vb.NET: ByVal vs ByRef

    Hi there!

    For the last few days I've been running my code through the code analyzer that comes with Visual Studio 2005. I've found a whole bunch of useful design recommendations produced by this tool but have been stumped by one of them.

    One of the warning messages recommends that I consider designing my subs and functions so that they do not accept ByRef arguments.

    One of my subs is meant to fill a DropDownList with values for the web page.
    Since DropDownLists with the same options are found in more than one location I've created a Sub that:
    -> accepts a DropDownList object,
    -> clears the list,
    -> and fills the list with values
    -> If the DropDownList supplied does not exist it create a new DropDownList and fill that one.

    Another of my functions returns true or false depending on if it executed correctly and also returns (ByRef) an object that has had 6 of its properties modified by the function.

    From what I understood I need to use the ByRef means of passing the argument in order to correctly pass back the information that I've modified.

    However, when I change the ByRef to ByVal, they still work properly.


    Looking up the difference between ByRef and ByVal in the help file I've found that:
    By Ref:
    -> If the procedure has a genuine need to change the underlying element in the calling code, declare the corresponding parameter ByRef.

    -> If the correct execution of the code depends on the procedure changing the underlying element in the calling code, declare the parameter ByRef. If you pass it by value, or if the calling code overrides the ByRef passing mechanism by enclosing the argument in parentheses, the procedure call might produce unexpected results.

    ByVal:
    -> If the underlying element is modifiable, but you do not want the procedure to be able to change its value, declare the parameter ByVal. Only the calling code can change the value of a modifiable element passed by value.


    So, according to the help, an argument passed ByVal should not result in the calling code's variable being modified by the sub/function.

    This Does Not appear to be the case.

    I am Very confused.

    Could someone please clarify?


    Thanks a lot

    -Frinny
  • bplacker
    New Member
    • Sep 2006
    • 121

    #2
    Byval is a way of passing parameters in by Value. This means that if you have 2 variables, a and b, and you pass them into a function with a=1 and b=2:

    private sub function1(byval a as integer, byval b as integer)
    a=50
    b=60
    end sub

    Once you get out of this function, a will still be = 1, and b will still be = 2. If you were to use ByRef instead of byval... once you get out of the function a would b = 50 and b would be = 60.

    Hope this helps.

    By the way, passing parameters by reference is much more costly and time consuming than passing them by value.

    Comment

    • RedSon
      Recognized Expert Expert
      • Jan 2007
      • 4980

      #3
      Originally posted by Frinavale
      Hi there!

      For the last few days I've been running my code through the code analyzer that comes with Visual Studio 2005. I've found a whole bunch of useful design recommendations produced by this tool but have been stumped by one of them.

      One of the warning messages recommends that I consider designing my subs and functions so that they do not accept ByRef arguments.

      One of my subs is meant to fill a DropDownList with values for the web page.
      Since DropDownLists with the same options are found in more than one location I've created a Sub that:
      -> accepts a DropDownList object,
      -> clears the list,
      -> and fills the list with values
      -> If the DropDownList supplied does not exist it create a new DropDownList and fill that one.

      Another of my functions returns true or false depending on if it executed correctly and also returns (ByRef) an object that has had 6 of its properties modified by the function.

      From what I understood I need to use the ByRef means of passing the argument in order to correctly pass back the information that I've modified.

      However, when I change the ByRef to ByVal, they still work properly.


      Looking up the difference between ByRef and ByVal in the help file I've found that:
      By Ref:
      -> If the procedure has a genuine need to change the underlying element in the calling code, declare the corresponding parameter ByRef.

      -> If the correct execution of the code depends on the procedure changing the underlying element in the calling code, declare the parameter ByRef. If you pass it by value, or if the calling code overrides the ByRef passing mechanism by enclosing the argument in parentheses, the procedure call might produce unexpected results.

      ByVal:
      -> If the underlying element is modifiable, but you do not want the procedure to be able to change its value, declare the parameter ByVal. Only the calling code can change the value of a modifiable element passed by value.


      So, according to the help, an argument passed ByVal should not result in the calling code's variable being modified by the sub/function.

      This Does Not appear to be the case.

      I am Very confused.

      Could someone please clarify?


      Thanks a lot

      -Frinny
      Frin,

      Look at it like this: The function is a black box
      Code:
      MyClass aClass = new MyClass
      
      SetDataOn(aClass);
      
      aClass = blackBox(aClass);
      
      ShowDataToUser(aClass)
      In this example it doesn't matter if the blackBox passes aClass by reference or by value because blackBox returns aClass back to the caller. So what you have is the object going into the blackBox, the blackBox doing something to it, then the object comes out.

      Now I will change it slightly:
      Code:
      MyClass aClass = new MyClass
      
      SetDataOn(aClass);
      
      int errorCode = blackBox(aClass);
      
      ShowDataToUser(aClass)
      Now it matters if aClass is passed to blackBox by reference or by value. If it is by value then the system will make a copy of aClass and give it to blackBox to do stuff with. When blackBox returns *your* copy of aClass remains unchanged because a copy was created and the changes to the copy never carried over to *your* copy. If it is passed by reference then the system gives blackBox the address of the original copy so blackBox can go out to the memory location where the original aClass object lives and do stuff to it there. To say it another way, blackBox gets passed the original object to play with instead of a copy. So when blackBox finishes it not only changed your aClass object but it also returns some interesting error code data for you to play with.

      Now this whole by value or by reference business can be quite complicated. In some languages the complier and system is not smart enough to copy everything properly if for example your class has some member data in it that is stored by reference then when the system copies it over it won't understand that those are reference parameters and it will treat them like data instead. You can get some weired stuff going on if that happens.

      Comment

      • AricC
        Recognized Expert Top Contributor
        • Oct 2006
        • 1885

        #4
        Frinny,
        Easy way to think of ByRef and ByVal. ByVal passes a copy of the variable, ByRef passes the original.

        Comment

        • Frinavale
          Recognized Expert Expert
          • Oct 2006
          • 9749

          #5
          Originally posted by AricC
          Frinny,
          Easy way to think of ByRef and ByVal. ByVal passes a copy of the variable, ByRef passes the original.
          Thank you all for your speedy replies!

          You've all just assured me that ByVal and ByRef are supposed to work the way I thought the were supposed to. ByVal passing a Value and ByRef passing a reference to the memory location where the object is stored.

          In my situation this does not appear to be the case.

          The following is a watered down version of what is occuring in my code.
          My question is why does object A in the MainClass.MainF unction() get set by the Utils.SetString () method:

          Code:
          Class MainClass
            Private A as New MyClass 'say A has 6 Strings that need to be set if the object is validated against the database
          
            Sub MainFunction()
                If Utils.SetStrings(A) then
                  ...
                else
                  ...
               end if
            End Function
          
          End Class
          
          Class Utils
              Public Function SetString(ByVal A as MyClass) As Boolean
                Dim retval as Boolan
                If CheckAgainstDatabase(A) = true Then 
                     A.String1="blaBla"
                     A.String2="blablablabla"
                     ...
                    retval = true
                  Else 
                    retval = false
                 End If
          
               Return retval
              End Function
          
              Private Function CheckAgainstDatabase(ByVal A as MyClass) As Boolean
                    '...validates the object agains the database 
              End Class
          End Class
          At first I had Utils.SetString (ByRef A as MyClass)...then the code analyzer complained and for fun I changed it to ByVal.......... .I was Shocked when it worked properly and all my strings were set in the calling class!

          -Frinny

          Comment

          • Frinavale
            Recognized Expert Expert
            • Oct 2006
            • 9749

            #6
            Originally posted by RedSon
            Frin,

            ...
            Now it matters if aClass is passed to blackBox by reference or by value. If it is by value then the system will make a copy of aClass and give it to blackBox to do stuff with. When blackBox returns *your* copy of aClass remains unchanged because a copy was created and the changes to the copy never carried over to *your* copy. If it is passed by reference then the system gives blackBox the address of the original copy so blackBox can go out to the memory location where the original aClass object lives and do stuff to it there. To say it another way, blackBox gets passed the original object to play with instead of a copy. So when blackBox finishes it not only changed your aClass object but it also returns some interesting error code data for you to play with.

            Now this whole by value or by reference business can be quite complicated. In some languages the complier and system is not smart enough to copy everything properly if for example your class has some member data in it that is stored by reference then when the system copies it over it won't understand that those are reference parameters and it will treat them like data instead. You can get some weired stuff going on if that happens.
            Your reply was very informative.
            This explains why the code analizer doesn't like me passing ByRef.
            I've come from a C++ background and have an idea of how copy constructors and such work...I've never seen a copy constructor declared in a VB.NET project.

            Is it even possible to declare a copy constructor in VB.NET?
            It would be really useful because then you'd be able to avoid the memory-addresse-aka-pointers-aka-references stored in your object being treated incorrectly by specifically telling the compiler what to do...

            I'm not storing any references in my object so I should be ok.

            Thanks for the heads up!

            -Frinny

            Comment

            • RedSon
              Recognized Expert Expert
              • Jan 2007
              • 4980

              #7
              Originally posted by Frinavale
              Thank you all for your speedy replies!

              You've all just assured me that ByVal and ByRef are supposed to work the way I thought the were supposed to. ByVal passing a Value and ByRef passing a reference to the memory location where the object is stored.

              In my situation this does not appear to be the case.

              The following is a watered down version of what is occuring in my code.
              My question is why does object A in the MainClass.MainF unction() get set by the Utils.SetString () method:

              Code:
              Class MainClass
                Private A as New MyClass 'say A has 6 Strings that need to be set if the object is validated against the database
              
                Sub MainFunction()
                    If Utils.SetStrings(A) then
                      ...
                    else
                      ...
                   end if
                End Function
              
              End Class
              
              Class Utils
                  Public Function SetString(ByVal A as MyClass) As Boolean
                    Dim retval as Boolan
                    If CheckAgainstDatabase(A) = true Then 
                         A.String1="blaBla"
                         A.String2="blablablabla"
                         ...
                        retval = true
                      Else 
                        retval = false
                     End If
              
                   Return retval
                  End Function
              
                  Private Function CheckAgainstDatabase(ByVal A as MyClass) As Boolean
                        '...validates the object agains the database 
                  End Class
              End Class
              At first I had Utils.SetString (ByRef A as MyClass)...then the code analyzer complained and for fun I changed it to ByVal.......... .I was Shocked when it worked properly and all my strings were set in the calling class!

              -Frinny
              This might be an artifact of the way that strings are implemented in VB. Usually strings are buffer of characters. The buffer is usually an array. In VB the system probably takes care of setting the size of the buffer and resizing it as necessary, or there is some maximum that it will fail on if it gets to large.

              Whats happening (I think) is that the system is treating strings as a special case and always passing by reference. Try doing the same thing with your above example but make the data in your class a char and see if that has the intended behavior.

              Comment

              • RedSon
                Recognized Expert Expert
                • Jan 2007
                • 4980

                #8
                Ah Frin, you are in luck.

                I found out the truth!

                When the VB.NET and VB6 compilers compile your code it makes certain optimization decisions about the way your code will be processed at run time. One of these decisions is to determine if a variable should be placed on the stack or heap, the heap in .NET is now referred to as the managed heap. Variables placed on the heap are considered referenced variables and as such, VB will not create a second instance of a referenced variable, regardless of the ByVal qualifier. When the compiler encounters a variable declaration, it determines if it is a fixed length, most primitive data types are fixed lengths. If the variable is of a fixed length, it is determined that the variable will be stored on the stack. Otherwise it is considered a reference variable and placed on the heap.

                The next question I would have is “What happens if I create a new local instance of the variable and assign the passed variable to it?”. You still get a reference. When the compiler encounters the assignment to a referenced variable it points it back to the original on the heap.

                And since strings are dynamic, they are put on the heap.

                Comment

                • Frinavale
                  Recognized Expert Expert
                  • Oct 2006
                  • 9749

                  #9
                  Originally posted by RedSon
                  Ah Frin, you are in luck.

                  I found out the truth!

                  When the VB.NET and VB6 compilers compile your code it makes certain optimization decisions about the way your code will be processed at run time. One of these decisions is to determine if a variable should be placed on the stack or heap, the heap in .NET is now referred to as the managed heap. Variables placed on the heap are considered referenced variables and as such, VB will not create a second instance of a referenced variable, regardless of the ByVal qualifier. When the compiler encounters a variable declaration, it determines if it is a fixed length, most primitive data types are fixed lengths. If the variable is of a fixed length, it is determined that the variable will be stored on the stack. Otherwise it is considered a reference variable and placed on the heap.

                  The next question I would have is “What happens if I create a new local instance of the variable and assign the passed variable to it?”. You still get a reference. When the compiler encounters the assignment to a referenced variable it points it back to the original on the heap.

                  And since strings are dynamic, they are put on the heap.
                  I don't know why I wasn't informed in my Control Panel that these posts were here! It could have saved me some time.

                  I also found out the same thing! Except I have a different explanation of what's going on.

                  Essentially I didn't understand what a Reference Variable Type was and what a Value Type Variable was....its the same as the difference between what a native type is and what an object type is in other languages...for some reason .NET decided to do away with these common concepts and create new terms for them.

                  Anyways, Reference Variable types are any objects created from a Class and also include Arrays and Delegates.

                  Reference Variable types exist on the Heap whereas Value Variable types do not. This means that if you create an instance of a Reference Variable and then assign it to another variable you'll get a copy of the pointer to the memory location where the object exists on the Heap.

                  If you have a Value Variable and assign it to another variable you get a copy of its value...not a copy of the pointer.

                  Since Reference Variable types are any objects created from a Class, my custom objects are automatically Reference Variable types.

                  Now when I pass them into a function using ByVal I'm actually getting a copy of the Pointer to my object on the heap.

                  This explains why when I'm using ByVal my object in the calling function was being effected by the changes in my worker function.

                  This is also why the code analyzer was complaining when I was using ByRef...its because I was passing my function a pointer to my pointer.

                  I wish I had seen your posts earlier because they would also have cleared up my confusion about how things were working.

                  Thanks a lot for your help RedSon!

                  -Frinny

                  Comment

                  • RedSon
                    Recognized Expert Expert
                    • Jan 2007
                    • 4980

                    #10
                    Sure thing Frin, glad to be of service ;)

                    Comment

                    Working...