How can a Sub update it parameters? - vb.net

I have some simple code and I understand what it does but not why. I have a Sub and it calls another Sub called CheckIfNothing(oList). oList is a List(Of String). The Sub CheckIfNothing checks each String and if it it Nothing it will make it "". This is the code:
Public Function GiveList(oList As List(Of String))
CheckIfNothing(oList)
Return oList
End Function
Public Sub CheckIfNothing(oList As List(Of String))
For Each s As String In oList
If s Is Nothing Then
s = ""
End If
Next
End Sub
So in GiveList I call CheckIfNothing and I don't return anything from CheckIfNothing and still, the oList in GiveList has no Strings that are Nothing.
I always thought you had to return the value you changed in the called function and set the value again in the sub you call the function in like this: oList = CheckIfNothing(oList). CheckIfNothing would be a function in this case.
Why isn't this necessary, and is this only in VB.NET or also the case in C#?

Maybe this will help explain your question. It is from MSDN regarding Visaul Basic 2013.
When passing an argument to a procedure, be aware of several different distinctions that interact with each other:
•Whether the underlying programming element is modifiable or nonmodifiable
•Whether the argument itself is modifiable or nonmodifiable
•Whether the argument is being passed by value or by reference
•Whether the argument data type is a value type or a reference type
For more information, see Differences Between Modifiable and Nonmodifiable Arguments (Visual Basic) and Differences Between Passing an Argument By Value and By Reference (Visual Basic).
This code is an example of how you can use () around your parameter to protect it from being changed.
Sub setNewString(ByRef inString As String)
inString = "This is a new value for the inString argument."
MsgBox(inString)
End Sub
Dim str As String = "Cannot be replaced if passed ByVal"
' The following call passes str ByVal even though it is declared ByRef.
Call setNewString((str))
' The parentheses around str protect it from change.
MsgBox(str)
' The following call allows str to be passed ByRef as declared.
Call setNewString(str)
' Variable str is not protected from change.
MsgBox(str)
Passing Arguments by Value and by Reference (Visual Basic) 2013

Related

Are all arrays forced to be ByRef when passed into a subroutines? [duplicate]

I have a Sub with an array of strings as parameter:
Private Sub des(ByVal array() As String)
Dim i As Integer
For i = 0 To UBound(array)
array(i) = "hy"
Next
End Sub
When I call the function inside my main function, the value of str changes even if the array is passed to the function ByVal:
Dim str() As String
str = {"11111", "22222", "33333", "44444", "5555", "66666"}
des(str)
I tried making a copy of the array in the Sub, but it still changes in the main function.
Private Sub des(ByVal array() As String)
Dim i As Integer
Dim array2() As String
array2 = array
For i = 0 To UBound(array)
array(i) = "hy"
Next
End Sub
I read on a site that you cannot pass arrays ByVal. Is this true? If so, how should I proceed?
Try this:
Dim i As Integer
Dim array2() As String
array2 = array.Clone()
For i = 0 To UBound(array2)
array2(i) = "hy"
Next
The key difference is the .Clone(), that actually makes a shallow copy of array, and changing the values in array2 will no longer affect your str value in the main code.
Arrays are reference types. That means that when you pass an Array to your function, what is passed is always a reference, not a copy of the array. The Array in your function refers to the same array object as the Array in your calling code.
The same thing happens when you do the assign (it is not a copy!) in your second example: all you've done is make yet another reference to the same object. That is why Boeckm's solution works -- the Clone() call does make a new array and assign it values which are copies of the values in the original array.
In Visual Basic .NET, regarding arrays as parameters, there are two important rules you have to be aware of:
Arrays themselves can be passed as ByVal and ByRef.
Arrays' elements can always be modified from the function or subroutine.
You already know that you can modify the elements of an array inside a subprocess (subroutine or function), no matter how that array is defined as parameter inside that subprocess.
So, given these two subroutines:
Private Sub desval(ByVal array() As String)
array = {}
End Sub
Private Sub desref(ByRef array() As String)
array = {}
End Sub
And this very simple auxiliary subroutine (here I'll use the Console):
Private Sub printArr(ByVal array() As String)
For Each str In array
Console.WriteLine(str)
Next
End Sub
you can do these simple tests:
Dim arr1() as String = {"abc", "xyz", "asdf"}
Console.WriteLine("Original array:")
printArr(arr1)
Console.WriteLine()
Console.WriteLine("After desval:")
desval(arr1)
printArr(arr1)
Console.WriteLine()
Console.WriteLine("After desref:")
desref(arr1)
printArr(arr1)
I read on a site that you cannot pass arrays ByVal. Is this true?
No, that is not true.
An array in the .NET framework is a reference type. When you create an array, an object of System.Array will be created and its reference is assigned to the reference variable.
When you call a des method, the reference of the array object will be passed. In des method, ByVal parameter is a reference parameter variable of type System.Array, and it receive a copy of reference of an array object.
MSDN article - Passing Arguments by Value and by Reference (Visual Basic)

Reference and Value types

I have read that String was a "reference type", unlike integers. MS website
I tried to test its behavior.
Sub Main()
Dim s As New TheTest
s.TheString = "42"
Dim z As String = s.GimmeTheString
z = z & "000"
Dim E As String = s.TheString
s.GimmeByref(z)
end sub
Class TheTest
Public TheString As String
Public Function GimmeTheString() As String
Return TheString
End Function
Public Sub GimmeByref(s As String)
s = TheString
End Sub
End Class
So I expected :
z is same reference as TheString, thus TheString would be set to "42000"
Then Z is modified by reference by GimmeByref thus Z is set to whatever TheString is
Actual result:
Z="42000"
E="42"
TheString="42"
What point am I missing?
I also tried adding "ByRef" in GimmeByRef : yes obviously the GimmeByRef does work as expected, but it also does if I put everything as Integer, which are said to be "Value type".
Is there any actual difference between those types?
The confusion comes about because regardless of type, argument passing in VB is pass by value by default.
If you want to pass an argument by reference, you need to specify the argument type as ByRef:
Public Sub GimmeByref(ByRef s As String)
You also need to understand the difference between mutating a value and re-assigning a variable. Doing s = TheString inside the method doesn’t mutate the value of the string, it reassigns s. This can obviously be done regardless of whether a type is a value or reference type.
The difference between value and reference types comes to bear when modifying the value itself, not a variable:
obj.ModifyMe()
Strings in .NET are immutable and thus don’t possess any such methods (same as integers). However, List(Of String), for instance, is a mutable reference type. So if you modify an argument of type List(Of String), even if it is passed by value, then the object itself is modified beyond the scope of the method.
Strings are immutable, every time you do a change it creates a new "reference" like if New was called.
A String object is called immutable (read-only), because its value
cannot be modified after it has been created. Methods that appear to
modify a String object actually return a new String object that
contains the modification. Ref
Your code basically does something like this:
Sub Main()
Dim a, b As String
a = "12"
b = a
a = a & "13"
Console.WriteLine(a) ' 1213
Console.WriteLine(b) ' 12
Console.ReadLine()
End Sub

VBA Complicated Getter, Setter syntax

Hi I'm rather new to VBA I need to create an object with relativelly complicated Getter and Setter. To do this I am constantly checking with MSDN but clearly I am not understanding something because VBE keep highlighting lines starting and closing: Property (it appaently needs Get or Let??), Get(it apparently needs identifier), Let(it apparently needs identifier as well).
But I am trying to follow more concise notation where Get and Let methods are within a Property Statement which is used by Microsoft in its examples(see link above).
Can someone tell me where is my syntax wrong(or Microsoft's Documentation for that matter)???
Thank you
Private Matrix() As Vector
Property Transition()
Public Get(Old_S As String, New_S As String, Period As Integer) As Double
' Some Code
Return Matrix(Column, Row).Value(Period)
End Get
Public Let(Old_S As String, New_S As String, Vector_String As String)
' Some Code
Matrix(Row, Column).Value = Vector_String
End Let
End Property
You are reading the documentation for VB.NET. That's why you are confused. The syntax for properties in VBA is different. In VBA, the Get and Let for a property are not grouped together. They need to be listed separately, essentially like two separate methods:
Private mMyProperty As String
Public Property Get MyProperty() As String
MyProperty = mMyProperty
End Property
Public Property Let Transition(Value As String)
mMyProperty = Value
End Property
For VBA reference material, try starting here.
Your issue is that you are reading .Net help files! :)
Assuming you have a valid Vector class, your properties need to bde defined like this:
Private Matrix() As Vector
Public Property Get Transition(Old_S As String, New_S As String, Period As Integer) As String
' Some Code
Transition = Matrix(Column, Row).Value(Period)
End Property
Public Property Let Transition(Old_S As String, New_S As String, Period As Integer, Vector_String As String)
' Some Code
Matrix(Row, Column).Value = Vector_String
End Property
Note that the argument list for both procedures must match except that the Letter has an additional argument of the same type that the Getter returns.
It is also slightly unusual to have a Getter property that accepts arguments in VBA - that would more usually be implemented as a method.
Looks like you're using VB.Net syntax in VBA. That's not going to work, as they're entirely different languages. Here is a link to the proper documentation for the VBA Property Keyword.
Here is how you would write it in VBA.
Private Matrix() As Vector
Public Property Get MatrixValue(Old_S As String, New_S As String, Period As Integer) As Double
' Some Code
MatrixValue = Matrix(Column, Row).Value(Period)
End Property
Public Property Let MatrixValue(Old_S As String, New_S As String, Vector_String As String)
' Some Code
Matrix(Row, Column).Value = Vector_String
End Property
But you're going to have trouble with the Get property. You can pass parameters into Get, but it's not exactly intuitive. I think what you're really looking for is a function.
Public Function GetMatrixValue(Old_S As String, New_S As String, Period As Integer) As Double
' Some Code
MatrixValue = Matrix(Column, Row).Value(Period)
End Function

ByRef in VB.NET

I have written the following code in VB.NET:
Dim obj As Object
obj = "00"
Test(obj)
MsgBox(obj)
Private Sub Test(ByRef num As Integer)
End Sub
Private Sub Test(ByVal num As Integer)
End Sub
When the value "00" is passed "ByRef" in the method "Test" it converts to 0. But if the value "00" is passed "ByVal" it keeps the same value as "00". How the passed value is being converted only depending of the signature?
In VB6 although the default passing type is "ByRef", still the same code keeps the same value("00")
Could anybody explain the reason behind this contradictory behaviour in VB6 and VB.NET?
The way you are doing it, the ByRef changes the type of the object from string to integer. By default, integer do not have trailling "0" when covnerted to strings.
This example below might help you understand what is hapenning.
Sub Main()
Dim o1 As Object = "00"
Dim o2 As Object = "00"
Console.WriteLine(o1.GetType().ToString())
Test1(o1)
Console.WriteLine(o1.GetType().ToString())
Console.WriteLine(o2.GetType().ToString())
Test2(o2)
Console.WriteLine(o2.GetType().ToString())
Console.ReadLine()
End Sub
Sub Test1(ByVal num As Integer)
End Sub
Sub Test2(ByRef num As Integer)
End Sub
Output
System.String
System.String
System.String
System.Int32
I suggest you always turn Option Strict On, this will remove a lot of confusion.
The object is of type System.String. It cannot be passed ByRef to a method, it is of the wrong type. So the compiler has to work around it and rewrites the code:
Dim obj As Object
obj = "00"
Dim $temp As Integer
$temp = CInt(obj)
Test($temp)
obj = $temp '' <=== Here
MsgBox(obj)
The indicated statement is the one that changes the object from a string to an integer. Which, converted again to a string by the MsgBox() call, produces "0" instead of "00".
Notable is that C# does not permit this and generate a compile error. This rewriting trick is rather nasty, if the method itself changes the original object then you'll have a very hard time guessing what is going on since that doesn't change the passed argument value.
ByRef means that value passes by reference and in function will be used the same value what has been sent.
ByVal means that value passes by value (function creates a copy of passed value) and you use only copy of value.

Sub / Function array parameter altered

I have a Sub with an array of strings as parameter:
Private Sub des(ByVal array() As String)
Dim i As Integer
For i = 0 To UBound(array)
array(i) = "hy"
Next
End Sub
When I call the function inside my main function, the value of str changes even if the array is passed to the function ByVal:
Dim str() As String
str = {"11111", "22222", "33333", "44444", "5555", "66666"}
des(str)
I tried making a copy of the array in the Sub, but it still changes in the main function.
Private Sub des(ByVal array() As String)
Dim i As Integer
Dim array2() As String
array2 = array
For i = 0 To UBound(array)
array(i) = "hy"
Next
End Sub
I read on a site that you cannot pass arrays ByVal. Is this true? If so, how should I proceed?
Try this:
Dim i As Integer
Dim array2() As String
array2 = array.Clone()
For i = 0 To UBound(array2)
array2(i) = "hy"
Next
The key difference is the .Clone(), that actually makes a shallow copy of array, and changing the values in array2 will no longer affect your str value in the main code.
Arrays are reference types. That means that when you pass an Array to your function, what is passed is always a reference, not a copy of the array. The Array in your function refers to the same array object as the Array in your calling code.
The same thing happens when you do the assign (it is not a copy!) in your second example: all you've done is make yet another reference to the same object. That is why Boeckm's solution works -- the Clone() call does make a new array and assign it values which are copies of the values in the original array.
In Visual Basic .NET, regarding arrays as parameters, there are two important rules you have to be aware of:
Arrays themselves can be passed as ByVal and ByRef.
Arrays' elements can always be modified from the function or subroutine.
You already know that you can modify the elements of an array inside a subprocess (subroutine or function), no matter how that array is defined as parameter inside that subprocess.
So, given these two subroutines:
Private Sub desval(ByVal array() As String)
array = {}
End Sub
Private Sub desref(ByRef array() As String)
array = {}
End Sub
And this very simple auxiliary subroutine (here I'll use the Console):
Private Sub printArr(ByVal array() As String)
For Each str In array
Console.WriteLine(str)
Next
End Sub
you can do these simple tests:
Dim arr1() as String = {"abc", "xyz", "asdf"}
Console.WriteLine("Original array:")
printArr(arr1)
Console.WriteLine()
Console.WriteLine("After desval:")
desval(arr1)
printArr(arr1)
Console.WriteLine()
Console.WriteLine("After desref:")
desref(arr1)
printArr(arr1)
I read on a site that you cannot pass arrays ByVal. Is this true?
No, that is not true.
An array in the .NET framework is a reference type. When you create an array, an object of System.Array will be created and its reference is assigned to the reference variable.
When you call a des method, the reference of the array object will be passed. In des method, ByVal parameter is a reference parameter variable of type System.Array, and it receive a copy of reference of an array object.
MSDN article - Passing Arguments by Value and by Reference (Visual Basic)