Recursive value in a function - vb.net

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

Related

How can I assign a value to a number of different variables in a collection using loops?

I have a problem that has been bugging me for a while now. Consider this code:
Public Class Class1
Dim VariableList as New List(of Object) From {MainForm.X, MainForm.Y,
SettingsForm.Z, SettingsForm.Textbox1.Text} '...etc.
Sub SetToZero()
For Each Element in VariableList
Element = 0
Next
End Sub
Sub SetToCustomValue(value As Double)
For Each Element in VariableList
Element = value
Next
End Sub
Sub LoadValuesFromFile()
Dim path As String = MainForm.GetPath()
For Each Element in VariableList
Element = File.Readline()
Next
End Sub
Sub SaveValuesToFile()
Dim path As String = MainForm.GetPath()
For Each Element in VariableList
Element = File.Writeline()
Next
End Sub
'and more similar functions/subs
As you can see, what this class does is that it takes lot of different variables from different places into a collection, and then various functions read or write values to every variable in that collection using loops. In this example, I have just a few variables, but most of the time there are dozens.
Reading the values is not a problem. Writing them, is, because when I declare that VariableList at the top of my class, that List just makes a copy of each variable, rather than maintaining a reference to it. Meaning that if, say, one of the functions modifies the MainForm.X in that List, the actual variable MainForm.X is not modified. To work with references, I would have to forgo loops, and assign every single variable manually, in every function. Which is obviously a lot of bad code. I want to declare that list of variables only once, and then use loops, like in this example code that I wrote above. My question is, how can I make such a container (List, Array, whatever) that would retain the references to the original variables in it, and make the code above possible?
There is no easy way to store pointers to variables in VB.NET. As a workaround, you can use a class to store your variables, as a class is always used as a pointer.
Here's an example of a way to achieve this with a ContainerClass which own a Dictionary of integers. One interest of this method would be that you can declare and name "variables" dynamically. In reality, they will be managed KeyValuePair. Once you have instantiated a copy of this class, you can use it to "manage" your variables by using this class as your pointer.
I included a loop which set all the integers to the same number just for fun, and to demonstrate the kind of manipulation which would end up having an effect similar to one of those described in your question.
Public Class Form2
'This is the container class which will be used to bypass the lack of pointers
'if you wanted to change a property, like the window width, it would be more difficult, but simples variables will be no trouble
Private variableContainer As New VariableContainer
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
variableContainer.AddVar("X", 5)
variableContainer.AddVar("Y", 15)
Debug.Print(variableContainer.GetVar("X"))
Debug.Print(variableContainer.GetVar("Y"))
variableContainer.SetAllVar(42)
Debug.Print("Next line will print 42")
Debug.Print(variableContainer.GetVar("X"))
End Sub
End Class
Public Class VariableContainer
'I know a public variable wouldn't need the fancy functions down there, but it's usually better to encapsulate, especially if you're working with a team
'and "future you" count as a teammate, never forget that...
Private list As New Dictionary(Of String, Integer)
Public Sub AddVar(ByVal name As String, ByVal value As Integer)
list.Add(name, value)
End Sub
Public Function GetVar(ByVal name As String) As Integer
If list.ContainsKey(name) Then
Return list(name)
Else
'I choose -1 arbitrarily, don't put too much thinking into this detail
Return -1
End If
End Function
Public Sub SetVar(ByVal name As String, ByVal num As Integer)
If list.ContainsKey(name) Then
list(name) = num
End If
End Sub
Public Sub SetAllVar(ByVal num As Integer)
Dim dict As New Dictionary(Of String, Integer)
For Each item As KeyValuePair(Of String, Integer) In list
dict.Add(item.Key, num)
Next
list = dict
End Sub
End Class
Have fun!

Get Label through Function

all! I'm developing a BlackJack game but I've run into a little bit of a problem. When calculating score, I have to type YourCard1.Text, YourCard2.Text, YourCard3.Text, etc.
Can I make a function that gets the right label each time it's called? I want to do this so I don't have to type so much...
For example, instead of typing out "YourCard1.Text", I want to be able to type "card(1)" Is this possible? I've tried multiple ways of doing this, but to no avail. I'm having trouble figuring out how to make it work.
Assuming you have those labels on your form, YourCard1.Text, YourCard2.Text, YourCard3.Text, etc., This function should work for you. It returns the Label itself, not the Text property.
Private Function card(index As Integer) As Label
Try
Return Me.Controls.
OfType(Of Label).
Where(Function(l) l.Name = "YourCard" & index.ToString()).
Single()
Catch
Return Nothing
End Try
End Function
Note: Me.Controls returns the controls directly inside the form, but doesn't return controls inside containers in the form. If your cards are inside a panel, Panel1 for example, you would do Return Panel1.Controls.OfType(Of Label)...
Usage:
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
card(1).Text = "Hello"
card(2).Text = "World"
End Sub
Edit to address comment.
You are pidgeonholed into only those semantics. So there is another way I could think of. But I wouldn't personally do this.
Public Class Form1
Private Class cardClass
Private myContainer As Control
Sub New(container As Control)
myContainer = container
End Sub
Default Public WriteOnly Property Item(ByVal index As Long) As String
Set(value As String)
card(index).Text = value
End Set
End Property
Private Function card(index As Integer) As Label
Try
Return myContainer.Controls.
OfType(Of Label).
Where(Function(l) l.Name = "YourCard" & index.ToString()).
Single()
Catch
Return Nothing
End Try
End Function
End Class
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim card As New cardClass(Me)
card(1) = "Hello"
card(2) = "World"
End Sub
End Class
The reason it's so complex is that though String is a reference type, it uses value type semantics. So when returning a string from a function, it can't refer back to the original memory location: it actually creates a copy of the string. So using function semantics won't work. Same would go for an array. It would be difficult (impossible?) to modify a string from either a function or array and have it modify the Label's Text property.

Assign a value to a shared variable?

I need help assigning a value to a public shared variable i made.
Public Shared craftability As String = "Craftable"
Private Sub CheckBox1_CheckedChanged(sender As Object, e As EventArgs) Handles CheckBox1.CheckedChanged
If CheckBox1.Checked = True Then
craftability = "Non-Craftable"
Else
craftability = "Craftable"
End If
When i try to use the value i assigned to it in another subclass it says:
Variable 'craftability' is used before it has been assigned a value. A null reference exception could result at runtime.
and returns a null value.
It looks like you've declared a Shared variable outside a class here. Shared variables are only used by instances of classes. For example if you want to track how many instances of a class there are, you would declare a shared variable within the class like this console app
Sub Main()
Dim t1 As New testclass
Dim t2 As New testclass
Console.WriteLine(t2.Count)
Console.ReadKey()
End Sub
Private Class testclass
Private Shared instanceCount As Integer
Public Sub New()
instanceCount += 1
End Sub
Public ReadOnly Property Count
Get
Return instanceCount
End Get
End Property
End Class
What you end up with is 2 being written to the console. And the shared variable is only accessible by each instance of the TestClass.
OK Static variables are like local variables declared in a procedure. However with regular local variables, when the procedure completes the local variable is destroyed. A static variable continues to exist and retains its most recent value. The next time you calls the same procedure, the static variable isn't declared again and reinitialized because it still exists and contains the latest value that you assigned to it. A static variable is only destroyed when the class or module that it was defined in is destroyed.
So your code should probably be
Private Sub CheckBox1_CheckedChanged(sender As Object, e As EventArgs) Handles CheckBox1.CheckedChanged
Static Dim craftability As String = "Craftable"
If CheckBox1.Checked = True Then
craftability = "Non-Craftable"
Else
craftability = "Craftable"
End If
However, looking at the code you've supplied, if you want craftability to be available throughout your class, then just declare it as you already have done - Outside a sub, but without the shared keyword. Without knowing how you want craftability to be used, I cant be more accurate I'm afraid. Hope this helps

How to use Function return

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

Cast of two custom types in VB.NET

I need to cast, in the better way, two objects of two types of two custom-classes (in VB.Net):
The code:
Public Class pluto
Public Sub New(ByVal campoPippoPass As String)
_campoPippo = campoPippoPass
End Sub
Private _campoPippo As String = ""
Public Property campoPippo() As String
Get
Return Me._campoPippo
End Get
Set(ByVal value As String)
If Not Object.Equals(Me._campoPippo, value) Then
Me._campoPippo = value
End If
End Set
End Property
End Class
Public Class pippo
Public Sub New(ByVal campoPippoPass As String)
_campoPippo = campoPippoPass
End Sub
Private _campoPippo As String = ""
Public Property campoPippo() As String
Get
Return Me._campoPippo
End Get
Set(ByVal value As String)
If Not Object.Equals(Me._campoPippo, value) Then
Me._campoPippo = value
End If
End Set
End Property
End Class
Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
Dim a As New pippo("ciao")
' here I have the error 'invalid cast'
Dim c As pluto = CType(a, pluto)
MsgBox(c.campoPippo)
End Sub
How can I convert "c" into an "a" type object? Is there another way than writing the following?
Dim c As New pluto(a.campoPippo)
In the case of a more complex class, it could be more easy if there was a function for the conversion.
Firstly, I'm assuming the line: Dim c As pluto = CType(b, pluto) is a mistype and should actually be Dim c As pluto = CType(a, pluto)?
You can't cast one class to another unless they're related. You might need to explain what you're trying to do otherwise my answer would be, why are you creating the different classes pluto and pippo if they seem to be identical? Just create the one class and create two objects of it.
If you do need separate classes, maybe they are related in some way and you could make pippo inherit from pluto? Or make both of them implement the same interface.
In general I'd also suggest that it might be worth translating your class/variable names to English since that might make it easier for people to understand what you're trying to do.