Understanding ByRef and ByVal

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))
    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)


        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
            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

Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

AndyAinscowFreelance programmer / ConsultantCommented:
>>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).
JedNebulaAuthor Commented:
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?


Open in new window

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.
Cloud Class® Course: Amazon Web Services - Basic

Are you thinking about creating an Amazon Web Services account for your business? Not sure where to start? In this course you’ll get an overview of the history of AWS and take a tour of their user interface.

Fernando SotoRetiredCommented:
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.

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
JedNebulaAuthor Commented:
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.
Fernando SotoRetiredCommented:
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
AndyAinscowFreelance programmer / ConsultantCommented:
>>I guess I need to remember to always clone my objects manually if I want a true Copy?

If you really require a copy.
AndyAinscowFreelance programmer / ConsultantCommented:
>>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.
JedNebulaAuthor Commented:
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.
AndyAinscowFreelance programmer / ConsultantCommented:
Jacques Bourgeois (James Burger)PresidentCommented:
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.
JedNebulaAuthor Commented:
Read and let your jaw hit the desk:

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.
AndyAinscowFreelance programmer / ConsultantCommented:
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.
JedNebulaAuthor Commented:
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.
Jacques Bourgeois (James Burger)PresidentCommented:
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.
JedNebulaAuthor Commented:
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.
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
.NET Programming

From novice to tech pro — start learning today.

Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.