In the below code I am not understanding the role played by ReadOnly. I elaborate my question below:
Structure Mutable
Private x As Integer
Public Function Mutate() As Integer
x = x + 1
Return x
End Function
End Structure
Module Module1
Public ReadOnly m As Mutable 'Statement 1
Sub Main()
Console.WriteLine(m.Mutate()) ' 1 Statement 2
Console.WriteLine(m.Mutate()) ' 1
Console.WriteLine(m.Mutate()) ' 1
Console.ReadLine()
End Sub
End Module
How the control behaves if the ReadOnly keyword was not there:
Statement 1 would read as: Public m As Mutable
Upon execution of statement 1, an instance “m” gets created in the stack memory which is large enough to fit an integer type value “x” in it. The default value of x is 0
m.Mutate() gets executed the first time. The control calls the function Mutate() where x = x+1 gets executed which increases the value of x by 1
The value of x = 1 is returned to the function Mutate() which is then displayed.
Steps 3 and 4 get repeated for the next two times when Mutate() function is called. The key point is that the value of x increases from 0 to 1, then from 1 to 2 and then finally to 3.
What is the difference when we add the ReadOnly keyword?
m.mutate() gets executed the first time which increases the value of x from 0 to 1. This is returned and “1” gets printed
But when the control shifts to the next line i.e. statement 2,and after the Mutate() function is called again I see from a break-point analysis that it is then the value of x is reset to 0 from 1. The function Mutate gets executed which increases the value again from 0 to 1 which gets printed. This is the main difference.
My Question:
Why does the presence of ReadOnly keyword reset the value of x to 0 everytime the function Mutate is called? In this case, “m” is a local variable storing an instance of the structure. When an instance of a structure is declared as ReadOnly in VB.Net what is the implication? In such cases, should I understand the role of ReadOnly as “write once and reset if called again”? Can someone please explain the point.
My confusion stems from the fact that ReadOnly in VB.Net can be only used on Properties and Member Variables (Fields). When that is the case, their values can be set in the constructor of that structure/class or at declaration. But here “m” stores an instance. So what changes are being introduced when ReadOnly is being used on an instance?
In short how should I understand why using the ReadOnly keyword gives the output as “1 1 1”?
Related
I have a few SQL tables, some which are linked, that I would like to query once and store locally in a single variable. I can't predict the length of the data ahead of time so I need a dynamic data structure.
Example data I'm querying:
Table 1
NameA
Red
Green
Blue
Table 2
NameA NameB
Red A
Red B
Red C
Blue D
Blue E
Green F
Table 3
NameA NameC
Red One
Blue Two
Blue Three
Blue Four
Blue Five
Green Six
Green Seven
I need to be able to filter and access NameB and NameC based on NameA values. I would prefer a nested dictionary structure where I could query like below:
Table1("0") 'will equal "Red"
Table2("Red")("0") 'will equal "A"
Table2("Blue")("1") 'will equal "E"
Table3("Green")("1") 'will equal "Seven"
'note: point here is data structure, not order of results
I have tried using VBA's nested dictionaries but have been unable to get around the lack of a "deep copy" function. One algorithm I wrote:
With SqlQueryResult
i = 0
Do Until .EOF
Call Table1.Add(CStr(i), .Fields(0).Value)
i = i + 1
.MoveNext
Loop
End With
For Each key In Table1.Keys
SqlQueryResult = GetResultsFromQuery(SELECT NameB WHERE NameA = Table1(key))
With SqlQueryResult
i = 0
Do Until .EOF
Call TempDict.Add(CStr(i), .Fields(0).Value)
i = i + 1
.MoveNext
Loop
End With
Set Table2(Table1(key)) = TempDict
TempDict.RemoveAll
Next key
Unfortunately assigning a Dict to another Dict only sets a reference and doesn't actually copy over data -- when I delete TempDict, the nested data from Table2 is also removed.
I also can't have a new dictionary per "branch" in the nest structure as I need this data to be available at a module-level scope, and therefore need to define these in the top of the module before program execution.
I've looked at multi-dimentional dynamic arrays - these can't be assigned to a parent structure like a dictionary. I also can't predict the size of each of these tables, e.g. Table1 might be 5/20/100/etc in size, Red may have 2/5/100/etcetc results in Table 2, Blue have 1/20/etcetc results in Table 2. Redim only works on a single dimension in an array.
I've had a brief look at Collections as well, and I am not sure these are viable.
I don't have much experience with classes and I would rather avoid a very involved process - I want it to be easy to add linked and unliked (i.e. data linked to Table 1, like Table 2 and 3, vs stand-alone data not related to any other table) to this program should I need to in the future. (My benchmark for "easy" is a pandas dataframe in python).
A simple wrapper class for scripting dictionaries which implements a clone method. This should work fine with primitive datatypes.
Option Explicit
Private Type State
Dict As scripting.Dictionary
End Type
Private s As State
Private Sub Class_Initialize()
Set s.Dict = New scripting.Dictionary
End Sub
Public Function Clone()
Dim myClone As scripting.Dictionary
Set myClone = New scripting.Dictionary
Dim myKey As Variant
For Each myKey In s.Dict
myClone.Add myKey, s.Dict.Item(myKey)
Next
Set Clone = myClone
End Function
Public Property Get Item(ByVal Key As Variant) As Variant
Item = s.Dict.Item(Key)
End Property
Public Property Set Item(ByVal Key As Variant, ByVal Value As Variant)
s.Dict.Item(Key) = Value
End Property
Public Sub Add(ByVal Key As Variant, ByVal Item As Variant)
s.Dict.Add Key, Item
End Sub
You will now be able to say
Set Table2.Item(Table1.Item(key)) = TempDict.Clone
Problem
I am testing the following simple VB.Net code:
For i = 0 To 5
Dim f As Integer
If i = 3 Then
f = 1
End If
Debug.WriteLine(f)
Next
and the output is as follows:
0
0
0
1
1
1
but i was expecting:
0
0
0
1
0
0
Solution
The simple way to solve it is to replace Dim f As Integer by Dim f As Integer = 0
Question
Is this the expected behavior and if so why?
This is documented in the Visual Basic Programming Guide:
Even if the scope of a variable is limited to a block, its lifetime is still that of the entire procedure. If you enter the block more than once during the procedure, each block variable retains its previous value. To avoid unexpected results in such a case, it is wise to initialize block variables at the beginning of the block.
That precisely describes the behaviour you're seeing.
I don't know why the language was designed that way - I prefer a language that doesn't let you use a variable without it being definitely assigned a value - but I wouldn't be surprised if it's just a matter of history and backward compatibility at this stage.
In VBA, you can treat a UDF name just like a Dimmed variable. For example
Function ADD(a, b) As Long
ADD = a
ADD = ADD + b
End Function
Where ADD stores the intermediate value as well as the end result. My question is; in terms of what's going on behind the scenes, is there any difference between storing a number/piece of data in a standard variable vs a function name variable.
I worry that perhaps the routine that called the function gets an update whenever the variable changes if you use the function name, rather than only when End Function is executed. IE. if you have some code
answer = ADD(1, 2)
then in memory answer gets written to twice, once when ADD = a, once when ADD = ADD + b. Of course we don't see this, because answer is left with whatever the final ADD value is
I ask because I often find I build up a function answer in stages using an intermediate variable, then pass that to the function name itself, where instead I could just write directly to the function name.
E.g.
Function ADD(a, b) As Long
Dim tot As Long
tot = a
tot = tot + b
ADD = tot
End Function
vs the first example. They acheive the same thing, in the second example tot represents the formula result, and so we need a final ADD = tot line at the end. For speed I would like to reduce any writes that are made, so is there any drawback in terms of speed, not readability to using the first method as opposed to declaring intermediates?
NB, to clarify, that's not all intermediates, I just mean the single intermediate that represents the function result, and could be replaced by the function name in the code.
In speed the first method should be slightly faster - you declare one variable less (but I doubt that someone would be able to notice it).
In general, using the first method can bring you to a recursion, if you are not careful (or if you are a VBA beginner):
Option Explicit
Dim lng10 As Long
Public Sub Test()
lng10 = 0
Debug.Print ADD2(1, 1)
End Sub
Function ADD2(a As Long, b As Long) As Long
lng10 = lng10 + 1
ADD2 = a + b
If lng10 < 10 Then
ADD2 = ADD2 + ADD2(1, 1)
End If
End Function
And if the recursion does not have a bottom, it would go to an overflow error.
With other words, this would be an runtime error:
Option Explicit
Dim lng10 As Long
Public Sub Test()
lng10 = 0
Debug.Print ADD2(1, 1)
End Sub
Function ADD2(a As Long, b As Long) As Long
lng10 = lng10 + 1
ADD2 = ADD2(a, b)
End Function
In the do while loop structure usually there's a part where you declare a variable equal to a number (in this case i) and then in a second part you make a increment (i+1). I've made this example in vba, but the structure could be repeated in several different programming languages like the for in php when you're getting data from a database. Now, what I would like to understand better is the relation between the previous mentioned declarations, that is i = some number and i = i + 1 . Wouldn't this generate a problem of interpretation since you're declaring a variable to something and then assigning a different value right after it? Is the second declaration of the variable value, i = i + 1, a new variable calling the previous one or both i's are the same? This is the general orientation I intend with this question. I think explaining the scoop of both variables would help understanding. Thanks!
Sub DoWhile()
Dim x, i, sum
x = 10
i = 1
sum = 0
Do While i < x
sum = sum + i
i = i + 1
Loop
MsgBox “Sum = ” & sum
End Sub
A variable is really just a location in memory. That location can have any value. By setting i=i+1, you're really saying "take the value at position i, add 1 to it, and store it at position i". No new variable is created. There's no problem with the computer interpreting this- what it cares about is the location of i, which isn't changing. It still knows where to find i, regardless of how many times you change the value there.
Since you have created the variable i as a global variable, any reference or modification to i in the sub will be on the same variable. That being said:
Dim i as int
i = 1
Do while i < 11
MsgBox("The value of i is: " & i)
i = i + 1
Loop
would display 10 messageboxes showing the value of i being between 1 and 10.
When the program encounters i = i + 1, the computer 'sees' this as take the value of i, add one to it, and store the result in the variable i.
Hope that helps.
Sub btn1_Click()
Static value As Integer
value = value + 1
MsgBox value
End Sub
I swear when I was taking a VB.net course in college there was a shorter way to tell a variable to add '' to itself. Maybe x=+1. I am using Access now though instead of visual studio. When I try that within the VBE it removes the +. I even removed Option Explicit with no change
Assuming the answer will be no, there is no way to short-hand it & its just a peculiarly of VBA
Sadly there are no operation-assignment operators in VBA.
(Addition-assignment += are available in VB.Net)
Pointless workaround;
Sub Inc(ByRef i As Integer)
i = i + 1
End Sub
...
Static value As Integer
inc value
inc value
If you want to call the incremented number directly in a function, this solution works bettter:
Function inc(ByRef data As Integer)
data = data + 1
inc = data
End Function
for example:
Wb.Worksheets(mySheet).Cells(myRow, inc(myCol))
If the function inc() returns no value, the above line will generate an error.