How to use Function return - vb.net

I'm wondering if this is the right way to do this.
I have this sample code that should represent a user action, and a calculation done in a function, this function should then return the value to my sub?
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim i As Integer = 1
Test_function(i)
MsgBox(i)
End Sub
Private Function Test_function(ByVal i As Integer)
i = i + 1
Return (i)
End Function
When i run this piece of code I get:
i = 1 in sub
i = 2 in function
i = 1 in sub?
How do you get the i = 2 into my sub? Or is this not the correct way of using this?

I think what you're asking is why does i not get changed by the call to Test_function.
Let's break down your code.
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim i As Integer = 1 'creating a variable named i, value 1.
Test_function(i) 'passing the value of the variable i to test function, but not doing anything with what the function returns.
MsgBox(i) 'calling messagebox with the value i.
End Sub
Private Function Test_function(ByVal i As Integer) 'creating a new variable also named i with the value being passed in.
i = i + 1 'incrementing the value of the i variable by 1.
Return (i) 'returning i
End Function
So there are a few concepts that you're misunderstanding as far as I can tell- what ByVal means, perhaps the concept of variable scoping, as well as what Return does.
The obvious answer is that you're not using the value returned by Test_function. If you had i = test_Function(i), then i would be incremented by the call to Test_function
Another approach would be to pass i ByRef instead of ByVal- if you did that, the i in the scope of your Test_function method would be the same as the i in the scope of your Button1_Click method. But because you are passing it ByVal, the i variables are actually two completely different variables.

There is nothing to wonder, it's only a simple misunderstanding. you are printing the actual value of i if you Call like this:
MsgBox(Test_function(i))
Then both will be the same;
or you can check this in following way:
Dim tempVal = Test_function(i)'<-- will give 2
MsgBox(i)'<--- will give 1
this is because you are passing the value of i not i as reference, if you pass it as reference then also both will be the same.
So if you change the function signature as follows:
Private Function Test_function(ByRef i As Integer)
i = i + 1
Return (i)
End Function
same function call will give you both values as 2
Dim tempVal = Test_function(i)'<-- will give 2
MsgBox(i)'<--- will give 2
Reference ByVal and ByRef

Related

Recursive value in a function

I am trying to develop a simple class-based function that will modify a previous value determined by the function, that is, it is a recurrence relationship.
In essence, I am developing my own random number generator which will work the same way the current Random class works, i.e.
Dim ran as New Random(123456)
For i = 0 To 9
MessageBox.Show(ran.NextDouble & " " & ran.Next(1,11))
Next
I can successfully do this using a class-based method simply by sending a value ByRef, but as you know for a method call, the old value to be modified needs to be placed inside the call to the method. Thus, I am trying to overcome use of a method or a global typed variable, and rather would like the instantiated class to somehow remember what the current value is.
The example code below attempts to multiply the value _value by 2 during every function call, so the expected result would be 2, 4, 8, 16, etc. However, even though a 2 is initially sent to the constructor, the value of _value is always returned as zero.
Class Example
Public _value As Integer
Public Sub New(ByVal _value)
End Sub
Public Function Value() As Integer
_value *= 2
End Function
End Class
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim x As New Example(2)
For i = 0 To 9
MessageBox.Show(x.Value)
Next
End Sub
Normally fields are Private. If you want to expose data from your class you would use a Public Property.
Change the name of the parameter for Sub New. If properly qualified your name will work but it is confusing. You must do something with the passed in value! Assign it to your field _value.
Your Function has no return value. It simply changes the value of _value. If you don't return anything use a Sub. Change the name of your Function to something meaningful. Add a Return statement to send a value back to the calling code.
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim x As New Example(2)
For i = 0 To 9
MessageBox.Show(x.DoubleValue.ToString)
Next
End Sub
Class Example
Private _value As Integer
Public Sub New(ByVal Input As Integer)
_value = Input
End Sub
Public Function DoubleValue() As Integer
_value *= 2
Return _value
End Function
End Class

VB dynamic parameters for functions

Is there any way to get the parameters of a function dynamic?
Like:
Function(text As String, If text = "yes" text2 As String)
End Function
So the second one just gets asked, when the first is filled or a certain value?
Optional is no way, cause for example the third needs to be filled when the second one is already.
No, you can't do that. But you might accomplish something similar via Currying.
Currying takes a function that needs multiple arguments, and converts into a set of functions that each need a single argument, where prior functions in the set return the next function in the set.
Where this will still be problematic for your situation is .Net likes strongly-typed delegates. If the initial function needs to return a method with one argument for any case, it must do that for all cases.
However, that doesn't mean it returns the same function. You can still vary which function is returned based on the initial input argument.
So instead of this:
Function test(text As String, a As String) As String
If text = "yes" Then
Return text & a
Else
Return a & text
End If
End Function
test("yes", "foo") ' produces "yesfoo"
test("no", "foo") ' produces "foono"
We have this:
Function test(text As String) As Func(Of String, String)
If text = "Yes" Then
Return Function(a) text & a
Else
Return Function(a) a & text
End If
End Function
test("yes")("foo") ' produces "yesfoo"
test("no")("foo") ' produces "foono"
This won't do exactly what you want; it still requires the additional argument or not in every situation. But it might give you enough new flexibility to accomplish your ultimate goal. For example, perhaps you could combine this with a closure, where you never use the additional argument, but in the case where you need it the additional value is still available. Exactly how this will look or whether it's really possible or helpful will depend on the details of what you're trying to accomplish beyond the simple test code in the question.
You could handle what to pass in the button code.
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim paramValue As String
If MyTestText = "yes" Then
paramValue = TextBox2.Text
Else
paramValue = TextBox1.Text
End If
End Sub
Private Function SomeFunction(text As String) As String
Dim ReturnValue As String = ""
'your code here
Return ReturnValue
End Function
Or you could pass both values and test in your Function code
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim ReturnedValue = SomeFunction(TextBox1.Text, TextBox2.Text)
End Sub
Private Function SomeFunction(text1 As String, text2 As String) As String
Dim ReturnValue As String = ""
If MyTestText = "yes" Then
'process text2
Else
'process text1
End If
Return ReturnValue
End Function
I am sure there are other ways to handle this.

Confused As To Why This Function Does Not Call?

I'm trying to call this function into a procedure and am confused on why this is not working, I've looked up how to format a function call and don't understand my mistakes.
Structure Stock
Dim category As String
Dim price As Integer
Dim size As String
Dim sku As String
Dim color As String
End Structure
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim stockArray() As String =
IO.File.ReadAllLines("stockInventory.txt")
End Sub
Private Sub btnNewSave_Click(sender As Object, e As EventArgs) Handles btnNewSave.Click
Dim answer
answer = check(sku, stockArray)
End Sub
Function check(sku, stockArray) As Boolean
Dim flag As Boolean
Dim numVar = -1
numVar = Array.IndexOf(stockArray, txtSKU.Text)
If numVar = -1 Then
flag = False
End If
If numVar <> -1 Then
flag = True
End If
Return flag
End Function
It gives me an error saying,
sku is not declared. It may be inaccessible due to its protection
level
I have a structure where sku is defined as being a string. Do I have to declare it again in this sub for it to work?
You can fine-tune this code a bit:
Function check(ByVal stockArray as String()) As Boolean
Return Array.IndexOf(stockArray, txtSKU.Text) >= 0
End Function
It'll return if the element is in the Array.
If your file is very very large and you are doing many searches, I recommend storing in a HashSet instead of an array to get O(1) searching, instead of the O(n) you are getting.
I notice your stock array, has to be declared at the class level, not the method level, to be accessible by other methods.

the function does not seem to change value when called upon 3 times

I am writing code in visual basic. the goal of the program is to get a function to randomly pick a string from a array when called upon. the problem is it is not choosing a different value when the button calls it to do so. I have no idea why it is doing this.
Also anyone know how to get multlines to print in a label or text? here is my code.
Public Class lbl
Public Function Noun() As String
Dim rand As New Random
Dim index As Integer
Dim nouns() As String = {"boy", "girl", "dog", "town", "car"}
Dim sentence As String ' used to build a sentence
index = rand.Next(5)
sentence = nouns(index)
Return sentence
End Function
Public Function outs(ByVal n1 As String) As String
Dim result = n1
Return result
End Function
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
lblRandom.Text = outs(Noun()) & outs(Noun())
lblRandom1.Text = outs(Noun())
End Sub
Public Shared Function RandomNumber(ByVal lowerBound As Integer, ByVal upperBound As Integer) As Integer
Return CInt(Math.Floor((upperBound - lowerBound + 1) * Rnd())) + lowerBound
End Function
Public Function Noun() As String
Dim rand As New Random
Dim index As Integer = -1
Dim nouns() As String = {"boy", "girl", "dog", "town", "car"}
Dim sentence As String ' used to build a sentence
index = RandomNumber(0, 4)
sentence = nouns(index)
Return sentence
End Function
Public Function outs(ByVal n1 As String) As String
Dim result = n1
Return result
End Function
Private Sub Button1_Click(ByVal sender As Object, ByVal e As EventArgs) Handles Button1.Click
lblRandom.Text = outs(Noun()) & outs(Noun())
lblRandom1.Text = outs(Noun())
End Sub
The problem is that you re-create a new random generator each time the Noun() function is called. The random number generator should be a global variable.
The problem is that the .NET random number generator generates random numbers based on a seed. The seed is an integer based on the current system date and time. If you call the random number generator a number of times in quick succession, the same seed is likely to be used and the same "random" number will be generated. The solution is to declare the Random object outside of the procedure in which you wish to generate the random number and to pass a seed to its constructor which is built from the current (random) date and time.
To answer your other questions. Label controls will automatically wrap text if the text contains carriage returns. I have added a carriage return below for lblRandom.
To wrap text in a text box, set its Multiline property to True.
The code that #Aly El-Haddad posted will also work but it contains some VB6 legacy code. I have tried to keep it as .NET as possible. I have also eliminated the superfluous "outs" procedure and made it flexible so that you can add or delete elements in the "nouns" array. Note that with the line "index = rand.Next(5)", you are including element 5 of the array which doesn't exist. The upper bound of the zero-based array is 4 so an "Index was outside the bounds of the array" error will be thrown when your random number generator returns 5.
Private myRandom As New Random(CType(DateTime.Now.Ticks Mod Int32.MaxValue, Integer))
Private Function Noun() As String
Dim index As Integer
Dim nouns() As String = {"boy", "girl", "dog", "town", "car"}
index = myRandom.Next(nouns.GetLowerBound(0), nouns.GetUpperBound(0))
Return nouns(index)
End Function
Private Sub Button1_Click(ByVal sender As Object, ByVal e As EventArgs) Handles Button1.Click
lblRandom.Text = Noun() & Constants.vbNewLine & Noun()
lblRandom1.Text = Noun()
End Sub

VB.net Logic Error

This is supposed to be a program that tells how long it would take you to download a given size of file after the user inputs their download speed. intOne doesn't seem to be affected at all by the function that I have created to determine what intOne's value should be. I'm reasonably sure that the equation I'm using is correct.
Public Class Form1
Private tSize As System.Drawing.Size
Private checkUserSpeed As Long = 0
Private checkBitByte As Integer = 0
Private setSize As Integer = 0
Private checkUserSize As String = ""
Private Answer As Double = 0
Private ReadOnly Property checkDwnldSize() As String
Get
Return ComboBox1.Text
End Get
End Property
Function checkDownloadSize(ByVal checkUserSize As String)
Dim suffixes As New List(Of String)({"b", "k", "m", "g", "t", "p", "e", "z"})
Dim index As Long = suffixes.IndexOf(checkUserSize.Substring(0, 1).ToLower) > -1
If index > -1 Then
Return checkUserSpeed / 1024 ^ index
Else
Return False
End If
End Function
Function checkForByte(ByVal checkUserSize)
If Microsoft.VisualBasic.Right(checkUserSize.ToLower, 7) = "byte(s)" Then
checkBitByte = 1
checkDownloadSize(checkUserSize)
Return True
End
Else
Return False
End If
Return checkBitByte
Return checkUserSpeed
End Function
Function checkForBit(ByVal checkUserSize)
If Microsoft.VisualBasic.Right(checkUserSize.ToLower, 6) = "bit(s)" Then
checkBitByte = 8
checkDownloadSize(checkUserSize)
Return True
End
Else
checkForByte(checkUserSize)
Return False
End If
Return checkBitByte
Return checkUserSpeed
End Function
Function Calculate(ByVal checkUserSpeed, ByVal checkUserSize)
checkForBit(checkUserSize)
Return Answer
End Function
Private Sub FitContents()
tSize = TextRenderer.MeasureText(TextBox3.Text, TextBox3.Font)
TextBox3.Width = tSize.Width + 10
TextBox3.Height = tSize.Height
End Sub
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Calculate(Convert.ToInt32(TextBox3.Text), ComboBox3.Text)
Answer = checkBitByte * ((1024 ^ setSize) / checkUserSpeed)
TextBox1.Text = Answer
End Sub
Private Sub TextBox3_TextChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles TextBox3.TextChanged
Call FitContents()
End Sub
Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
Form2.Show()
End Sub
Private Sub Button3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button3.Click
Form3.Show()
End Sub
End Class
Help would be greatly appreciated.
You never assign any value to intOne, so it starts with value 0. So this:
intOne = intOne / 1024 ^ 3
is the same as this:
intOne = 0 / 1024 ^ 3
which is of course this:
intOne = 0
As an aside:
Don't use Dim at the class/form level, use Private.
Always use As <ReturnType> when decalring you functions
Turn on Option Strict in your language settings.
Dim intTwo As Integer = Nothing doesn't do anything useful. Use
Dim intTwo As Integer = 0 instead.
Always declare your variables as a type as well (Dim tSize is missing its type)
Followup to your edits:
This:
Function checkForByte(ByVal checkUserSize)
Should be this:
Function checkForByte(ByVal checkUserSize As Long)
Fix this in all places in your code. Everything should have an As DataType. If you turned on Option Explict and Option Strict like was suggested the compiler will locate these problems for you.
Next, do not name local variables and/or parameters the same as your class/form level private variables. I personally prefix my instance variables like this:
Private _checkUserSpeed As Long = 0
Note the underscore. Some people use a lowercase m instead:
Private mCheckUserSpeed As Long = 0
Pick something and be consistent. This makes it clear the scope of a variable.
Also this code is bad, the second Return will never be hit:
Return checkBitByte
Return checkUserSpeed
Finally, assuming you are trying to populate the variable _checkUserSpeed with a value, you are still not ever doing it. Search your code and look for any place you are doing this:
_checkUserSpeed = something
You won't find it. So that variable will always be 0.
None of these are really VB.NET specific issues. All of these best-practices would apply equally to C# or any other langue.
You should look into cleaning up your code first, to improve readability. Avoid code repetition at all costs. If you think you cannot do otherwise, lean towards data repetition instead, i.e. create an XML file to store your values, but keep the code clean. Any errors you find later will then stand out like white crows.
For example, something like this:
Function checkDownloadSize(ByVal strOne As String)
Dim suffixes As New List(Of String)({"b", "k", "m", "g", "t", "p", "e", "z"})
Dim index As Long = suffixes.IndexOf(strOne.Substring(0, 1).ToLower) > -1
If index > -1 Then
Return intOne / 1024 ^ index
Else
Return False
End If
End Function
roughly replaces your two pages of checkDownloadSize and checkSize.
Notice how every function here is called only once. If you later decide to swap ToLower with your custom implementation, it needs to be done only once. Not only a performance improvement, it also makes your code more maintainable in the long run.
Another point is that I am not using Microsoft.VisualBasic namespace in the above code, and switched to Substring instead. This will ease your burden with migration to C#, and also make native C# devs understand your code better.