Solved

Understanding ByRef and ByVal

Posted on 2014-11-05
16
90 Views
Last Modified: 2014-11-19
I am now using Visual Studio .Net 2013 for my VB projects.

Am I right in thinking that the parameter default has changed from ByRef to ByVal - like it used to be back in the old VB6 days? I could have sworn the default was ByRef when I first started .Net programming.

I am just looking for some clarification really on how I should expect the objects to be treated now.

I put together the following test code below.

The results I got back were:

int1 - 50
int2 - 30
list count - 4
class test - 50

The Int test worked as I would expect, but I got the same results for the List(Of and class test whether I used ByRef or ByVal.

What are the 'rules' for knowing if changes to the parameter will be passed back to the calling routine or not?

Public Class Form1

    Sub ByValListPassMe(ByVal lst As List(Of String))
        lst.Add("new")
    End Sub
    Sub ByRefPassMe(ByRef a As Int16)
        a = 50
    End Sub

    Sub ByValPassMe(ByVal a As Int16)
        a = 50
    End Sub

    Sub ClassTest(ByVal pstClass As clsTest)
        pstClass.Val = 50
    End Sub

    Private Sub btnByRefTest_Click(sender As Object, e As EventArgs) Handles btnByRefTest.Click
        Dim list As New List(Of String)
        Dim int1 As Int16 = 30
        Dim int2 As Int16 = 30
        Dim myValue As New clsTest(30)

        list.Add("one")
        list.Add("two")
        list.Add("three")

        ByRefPassMe(int1)
        ByValPassMe(int2)
        ByValListPassMe(list)
        ClassTest(myValue)
        Stop 'to enable me to clear the immediate window.

        Debug.Print("int1 - " & int1)
        Debug.Print("int2 - " & int2)
        Debug.Print("list count - " & list.Count)
        Debug.Print("class test - " & myValue.Val)

    End Sub
End Class

Public Class clsTest
    Implements ICloneable

    Private intVal As Int16
    Sub New(_Val As Int16)

        intVal = _Val

    End Sub

    Property Val As Int16
        Get
            Return intVal
        End Get
        Set(value As Int16)
            intVal = value
        End Set
    End Property

    Public Function Clone() As Object Implements ICloneable.Clone
        Return Me.MemberwiseClone
    End Function
End Class

Open in new window

0
Comment
Question by:JedNebula
  • 6
  • 5
  • 2
  • +2
16 Comments
 
LVL 44

Expert Comment

by:AndyAinscow
Comment Utility
>>but I got the same results for the List(Of and class test whether I used ByRef or ByVal.

My understanding is that a class (eg List) is always ByRef no matter what you specify.  (The ByVal would force some sort of copy to take place, for structures and simple objects that is done, for classes it is suppressed).
0
 
LVL 1

Author Comment

by:JedNebula
Comment Utility
I tried allowing my class to support cloning as I thought it then might clone/copy the object when ByVal was used, but it appears not.

I guess I need to remember to always clone my objects manually if I want a true Copy?

ClassTest(myValue.Clone)

Open in new window

0
 
LVL 11

Expert Comment

by:louisfr
Comment Utility
In .NET, there are two kind of types.
Value types, such as Integer, or the types defined using the Struct keyword. Reference types, such as String, or the types defined using the Class keyword.
A value-type variable contains a value. A reference-type variable contains a reference to a value.

A simple operation like a=b always copy the contents of the variable. If it's a value-type variable, the contents is the value, and you obtain a copy of the value. If it's a reference-type variable, the contents is a reference to a value, and you obtain a copy of the reference to the same value.

A ByVal parameter works the same way as a simple assignment: the value is copied if it's a value-type parameter, the reference to the value is copied if it's a reference-type parameter.
A ByRef parameter simply declares an alias to the variable used in the method call.

When your method uses
pstClass.Val = 50

Open in new window

it sets the Val property of the object referenced by pstClass.
If you tried to modify the parameter, like
pstClass = new clsTest(50)

Open in new window

, you would see the ByVal doing its work.
0
 
LVL 62

Accepted Solution

by:
Fernando Soto earned 500 total points
Comment Utility
Hi  JedNebula;

Value types such as Integer, Double, Struct and other basic types when passed by value and changes made to the parameter within the function, those changes will NOT be reflected in the calling code. But when passed by reference those changes will be reflected in the calling code.

When passing a parameter to a function of a reference type such as Class object works a little different. Because a reference type is an actual address in memory when you modify the value of a reference type that is passed in by value you will be able to modify the values of that class and will be seen outside that function with the changes but what you will not be able to do is replace that object with in the function with a new object of the same type. If you were to pass in the parameter by reference not only will you be able to modify the class as in passing it in by value but if you were to create a new object of the same type and replace the parameter with this new type it will also be seen outside the function.
0
 
LVL 1

Author Comment

by:JedNebula
Comment Utility
Thank you everyone - very interesting.

Fernando Soto - I think I understand what you're saying.

I modified my Class modifying sub to:

    Sub ClassTest(ByVal pstClass As clsTest)
        'pstClass.Val = 50
        pstClass = New clsTest(80)
    End Sub

Open in new window


and 30 was returned, which is what you were saying, but 80 was returned with ByRef. I didn't know this.

The reason I ask, is that I have been having trouble with memory leaks recently.

Some class procedures I have accept DataTables as parameters and often they are quite large - many thousand rows in some cases.

I wanted to understand what stopped the GC from clearing up objects and I feared it might have been because I had passed a reference to the DataTable whereas I should be using dt.Copy and thus freeing up any links to the object when it is Disposed.
0
 
LVL 62

Expert Comment

by:Fernando Soto
Comment Utility
To your statement, "I guess I need to remember to always clone my objects manually if I want a true Copy?", be aware that using .Net methods such as Object.MemberwiseClone will do a shallow copy of the object. But if your object which you wish to have a new object of has reference type in it those objects will be referencing the same object as the original object. Please read the following Microsoft documentation.

Object.MemberwiseClone Method
0
 
LVL 44

Expert Comment

by:AndyAinscow
Comment Utility
>>I guess I need to remember to always clone my objects manually if I want a true Copy?

If you really require a copy.
0
 
LVL 44

Expert Comment

by:AndyAinscow
Comment Utility
>>The reason I ask, is that I have been having trouble with memory leaks recently.

Be careful, the GC only runs when the system instructs it to run NOT when an object goes out of scope.  So memory usage can increase without there being a memory leak.  There is however one case where by design (from Microsoft) memory is never freed up until the app quits - which can lead to apps crashing through lack of memory.
0
How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

 
LVL 1

Author Comment

by:JedNebula
Comment Utility
be aware that using .Net methods such as Object.MemberwiseClone will do a shallow copy of the object.

Yes that's okay. I'm okay with copying the children items as well.

the GC only runs when the system instructs it to run NOT when an object goes out of scope
I understand this too, but my problem was that they were not being collected even after a call to GC.Collect

I am using IDisposable on objects now and I think I have it sorted, but I just wanted to check.
0
 
LVL 44

Expert Comment

by:AndyAinscow
Comment Utility
0
 
LVL 40

Expert Comment

by:Jacques Bourgeois (James Burger)
Comment Utility
Some class procedures I have accept DataTables as parameters and often they are quite large - many thousand rows in some cases. I wanted to understand what stopped the GC from clearing up objects.

By default, the GC clears object when it is out of resources.

You will notice that most big objects, such as DataTables, have a Dispose method. It is recommended to call Dispose when you are finished with the object, and before it goes out of scope.

When a big class that requires cleanup is designed properly (and the Framework is) and according to the conventions, the programmer who designed the class will create a Dispose method and make it so that the GC calls it when it needs to free memory. But it is also designed so that a user can call it to force the class to perform its cleanup as soon as possible instead of waiting for garbage collection.

An alternative is to declare the variable with a Using...End Using structure. This will call Dispose automatically at the End Using. It makes it easier when the structure of the method where you use the class is complex ( (numerous If and Select Case)) and you would have to call Dispose in many places to make sure that it is called.
0
 
LVL 1

Author Comment

by:JedNebula
Comment Utility
Read and let your jaw hit the desk:
https://www.simple-talk.com/dotnet/.net-framework/the-dangers-of-the-large-object-heap/

Yep, it hit the desk alright.

Thanks for the post - at least I can now understand why I am getting OutOfMemory Exceptions despite clearing up everything to the best of my ability.

I now have to look at avoiding adding to this problem.

The App I am maintaining currently, is designed to run tasks at regular timed intervals throughout the day and should stay open at all times.

Some of these tasks are for comparing MS SQL tables locally with MySQL tables used externally on websites. Some of these have many thousands of rows.

For these types of task, I have written a (now rather large) class that will allow this to happen, but I know that it is perhaps not the most effective way of doing things.

In a nutshell, it reads the source and destination tables into two DataTables and then compares the differences (row by row) and creates any required update, insert and delete statements before executing them.

I have incorporated into the class, the ability to read the tables in chunks (using ROWNUMBER()) but there is one issue with this as I will try to explain. I have a boolean property called DeleteUnmatchedRecords. When this is set to True, the class will remove from the destination DataTable, any rows that were not included in the compare. (Basically, rows that have since been deleted in the MS SQL database and need to be removed in the dest too).

When I have the whole dataset in memory, I can easily add a column to the destination DataTable to mark True, all the rows that I deal with in the compare. Any that are NULL at the end, I know to delete. If I read the table in chunks, I'm not sure how to do this.

Sorry - I know I have gone way off topic now.
0
 
LVL 44

Expert Comment

by:AndyAinscow
Comment Utility
Off the cuff.
Could you have a second app as well?  Your main app runs continuously.  At intervals it spawns the second app which will do the comparison, send something to the main app to indicate a status if required, then exit.  This should avoid the continuously running app to handle large data tables in memory.
0
 
LVL 1

Author Comment

by:JedNebula
Comment Utility
Thanks AndyAinscow,

The app has a status form, which is basically a RichTextBox outputting tasks details for the user to see.

I'd rather keep it all as one app if possible, but makes sense as a last resort.
0
 
LVL 40

Expert Comment

by:Jacques Bourgeois (James Burger)
Comment Utility
run tasks at regular timed intervals

This is most often better done by running the applications through TaskScheduler instead of having it to run all the time. Doing so, if you forgot to free up something, the GC will eventually catch it because it knows that it is no longer in use when the application is closed.
0
 
LVL 1

Author Closing Comment

by:JedNebula
Comment Utility
Sorry for the delay on closing this one. I got sidetracked on other projects.

Thank you everyone for your help in my 'escalating... off-topic' comments. The memory stuff was very interesting, but I thought the points should go to a post that was relevant to the original question.

Thanks again.
0

Featured Post

IT, Stop Being Called Into Every Meeting

Highfive is so simple that setting up every meeting room takes just minutes and every employee will be able to start or join a call from any room with ease. Never be called into a meeting just to get it started again. This is how video conferencing should work!

Join & Write a Comment

In my previous two articles we discussed Binary Serialization (http://www.experts-exchange.com/A_4362.html) and XML Serialization (http://www.experts-exchange.com/A_4425.html). In this article we will try to know more about SOAP (Simple Object Acces…
Creating an analog clock UserControl seems fairly straight forward.  It is, after all, essentially just a circle with several lines in it!  Two common approaches for rendering an analog clock typically involve either manually calculating points with…
This video discusses moving either the default database or any database to a new volume.
This video shows how to remove a single email address from the Outlook 2010 Auto Suggestion memory. NOTE: For Outlook 2016 and 2013 perform the exact same steps. Open a new email: Click the New email button in Outlook. Start typing the address: …

728 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

9 Experts available now in Live!

Get 1:1 Help Now