Home > Software engineering >  ByVal behaving like ByRef
ByVal behaving like ByRef

Time:04-13

Perhaps this is a bug in the (current version of) Visual Studio 2022 ... but according to everything I had understood about ByVal vs ByRef up until now, as well as this doc:

https://docs.microsoft.com/en-us/dotnet/visual-basic/programming-guide/language-features/procedures/passing-arguments-by-value-and-by-reference#:~:text=The advantage of passing an,being changed by the procedure.

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

I would expect this code:

Friend Sub Driver()

    Dim index As Integer = 5

    Dim OneDimensionalMatrix(index) As Integer

    For x As Integer = 0 To index - 1
        OneDimensionalMatrix(x) = 44
    Next

    SubRoutine(index, OneDimensionalMatrix)

    Console.WriteLine(index)

    Console.WriteLine(OneDimensionalMatrix(3))

End Sub

Public Sub SubRoutine(ByVal matrixSize As Integer, ByVal OneDimensionalMatrix() As Integer)

    For x As Integer = 0 To matrixSize - 1

        OneDimensionalMatrix(x) = 55

    Next

    matrixSize = 100

End Sub

Would generate an output of:

5

44

but it is generating an output of:

5

55

Which to me means the ByVal for the OneDimensionalMatrix is being handled as if it was a ByRef declaration.

Am I missing something, or is this a bug in VS 2022?

CodePudding user response:

Here are the very simple rules for passing by value or by reference:

  1. When passing a value type (structure) by value, neither modifying the object nor assigning a new object will affect the original variable.
  2. When passing a value type by reference, both modifying the object and assigning a new object will affect the original variable.
  3. When passing a reference type (class) by value, modifying the object will affect the original variable but assigning a new object will not.
  4. When passing a reference type by reference, both modifying the object and assigning a new object will affect the original variable.

There is no way to pass a reference type object without modifications to the object affecting the original variable. If that's what you want then it's up to you to explicitly create a copy of the object and pass that into the method.

Passing a method argument by value is exactly like assigning to another variable. It creates a copy of the contents of the original variable. If that contents is a value then the value is copied and if that contents is a reference then the reference is copied. If you copy the reference then you have two references to the same object, not two objects. What would you expect to be output here:

Dim arr1 = {1, 2, 3}
Dim arr2 = arr1

arr2(1) = 5

Console.WriteLine(arr1(1))

Hopefully you said "5" because hopefully you realise that both arr1 and arr2 refer to the same array object. The same goes when you pass an array - or any reference type object - to a method by value.

This behaviour is exactly why ByVal is the default in VB.NET where it was ByRef in VB6. In VB6, large objects were copied by default, so you had to pass by reference to avoid that. In VB.NET, large objects are not copied by default, so you only pass by reference is you actually want to pass a different object out than you passed in. The fact that value types are copied by default is one of the main reasons that value types should only ever be small. I believe the recommendation is no larger than 32 bytes, i.e. a maximum of 8 fields.

  • Related