I am making loop to make an array of Timers and give each timer a function
here's something like what i did:
dim timer(10) as Timer
for i = 0 to 5
timer(i) = new Timer
AddHandler timer(i).Tick, Function(senderX, eX) timerFunction(i)
next
i have this function:
Private Function timerFunction(ByVal timerNo As Integer)
MsgBox(timerNo)
End Function
but i am getting 6 as the value of timerNo for every timer i call with this:
timer(3).Start()
i outputs 6 even in i change the parameter to a number from 1 to 5
why is it diong that?
You have "closed over the loop variable". The value of timerNo is evaluated at the time the function is called which is always after the loop has completed, so the value of timerNo will always be 6.
You should have got a compiler warning: "BC42324 Using the iteration variable in a lambda expression may have unexpected results. Instead, create a local variable within the loop and assign it the value of the iteration variable."
To do this with your example...
Dim timer(10) As Timer
For i As Integer = 0 To 5
Dim j As Integer = i
timer(i) = New Timer
timer(i).Interval = 1000
timer(i).Enabled = True
AddHandler timer(i).Tick, Function(senderX, eX) timerFunction(j)
Next
Function timerFunction(timerNo As Integer) As String
MsgBox(timerNo)
Return timerNo.ToString
End Function
This way, a new instance of j is created for each iteration of the loop.
Related
How do I return a result from a function?
For example:
Public Function test() As Integer
return 1
End Function
This gives a compile error.
How do I make this function return an integer?
For non-object return types, you have to assign the value to the name of your function, like this:
Public Function test() As Integer
test = 1
End Function
Example usage:
Dim i As Integer
i = test()
If the function returns an Object type, then you must use the Set keyword like this:
Public Function testRange() As Range
Set testRange = Range("A1")
End Function
Example usage:
Dim r As Range
Set r = testRange()
Note that assigning a return value to the function name does not terminate the execution of your function. If you want to exit the function, then you need to explicitly say Exit Function. For example:
Function test(ByVal justReturnOne As Boolean) As Integer
If justReturnOne Then
test = 1
Exit Function
End If
'more code...
test = 2
End Function
Documentation: Function Statement
VBA functions treat the function name itself as a sort of variable. So instead of using a "return" statement, you would just say:
test = 1
Notice, though, that this does not break out of the function. Any code after this statement will also be executed. Thus, you can have many assignment statements that assign different values to test, and whatever the value is when you reach the end of the function will be the value returned.
Just setting the return value to the function name is still not exactly the same as the Java (or other) return statement, because in java, return exits the function, like this:
public int test(int x) {
if (x == 1) {
return 1; // exits immediately
}
// still here? return 0 as default.
return 0;
}
In VB, the exact equivalent takes two lines if you are not setting the return value at the end of your function. So, in VB the exact corollary would look like this:
Public Function test(ByVal x As Integer) As Integer
If x = 1 Then
test = 1 ' does not exit immediately. You must manually terminate...
Exit Function ' to exit
End If
' Still here? return 0 as default.
test = 0
' no need for an Exit Function because we're about to exit anyway.
End Function
Since this is the case, it's also nice to know that you can use the return variable like any other variable in the method. Like this:
Public Function test(ByVal x As Integer) As Integer
test = x ' <-- set the return value
If test <> 1 Then ' Test the currently set return value
test = 0 ' Reset the return value to a *new* value
End If
End Function
Or, the extreme example of how the return variable works (but not necessarily a good example of how you should actually code)—the one that will keep you up at night:
Public Function test(ByVal x As Integer) As Integer
test = x ' <-- set the return value
If test > 0 Then
' RECURSIVE CALL...WITH THE RETURN VALUE AS AN ARGUMENT,
' AND THE RESULT RESETTING THE RETURN VALUE.
test = test(test - 1)
End If
End Function
How do I return a result from a function?
For example:
Public Function test() As Integer
return 1
End Function
This gives a compile error.
How do I make this function return an integer?
For non-object return types, you have to assign the value to the name of your function, like this:
Public Function test() As Integer
test = 1
End Function
Example usage:
Dim i As Integer
i = test()
If the function returns an Object type, then you must use the Set keyword like this:
Public Function testRange() As Range
Set testRange = Range("A1")
End Function
Example usage:
Dim r As Range
Set r = testRange()
Note that assigning a return value to the function name does not terminate the execution of your function. If you want to exit the function, then you need to explicitly say Exit Function. For example:
Function test(ByVal justReturnOne As Boolean) As Integer
If justReturnOne Then
test = 1
Exit Function
End If
'more code...
test = 2
End Function
Documentation: Function Statement
VBA functions treat the function name itself as a sort of variable. So instead of using a "return" statement, you would just say:
test = 1
Notice, though, that this does not break out of the function. Any code after this statement will also be executed. Thus, you can have many assignment statements that assign different values to test, and whatever the value is when you reach the end of the function will be the value returned.
Just setting the return value to the function name is still not exactly the same as the Java (or other) return statement, because in java, return exits the function, like this:
public int test(int x) {
if (x == 1) {
return 1; // exits immediately
}
// still here? return 0 as default.
return 0;
}
In VB, the exact equivalent takes two lines if you are not setting the return value at the end of your function. So, in VB the exact corollary would look like this:
Public Function test(ByVal x As Integer) As Integer
If x = 1 Then
test = 1 ' does not exit immediately. You must manually terminate...
Exit Function ' to exit
End If
' Still here? return 0 as default.
test = 0
' no need for an Exit Function because we're about to exit anyway.
End Function
Since this is the case, it's also nice to know that you can use the return variable like any other variable in the method. Like this:
Public Function test(ByVal x As Integer) As Integer
test = x ' <-- set the return value
If test <> 1 Then ' Test the currently set return value
test = 0 ' Reset the return value to a *new* value
End If
End Function
Or, the extreme example of how the return variable works (but not necessarily a good example of how you should actually code)—the one that will keep you up at night:
Public Function test(ByVal x As Integer) As Integer
test = x ' <-- set the return value
If test > 0 Then
' RECURSIVE CALL...WITH THE RETURN VALUE AS AN ARGUMENT,
' AND THE RESULT RESETTING THE RETURN VALUE.
test = test(test - 1)
End If
End Function
I call a subroutine MyPartsMatrix inside nested Parallel.For loops (vb.net). MyPartsMatrix requires a variable called "unfilled" that is passed ByRef because this value is modified inside MyPartsMatrix subroutine. I need to grab and store this value after the subroutine MyPartsMatrix executes.
The "unfilled" variable yields a different value when I run the parallel version of this code compared to a one that is non-parallel, using normal nested For...Next loops. I can't figure out why this is the case.
Is it thread safe to call another subroutine from inside the Parallel.For loop?
Is this variable "unfilled" thread safe?
Dim ConcurrentListofResults As ConcurrentQueue(Of FindBestResults)
ConcurrentListofResults = New ConcurrentQueue(Of FindBestResults)
Dim x = 5, y = 5
Parallel.For(0, x, Sub(oD)
Parallel.For(0, y, Sub(oT)
Dim unfilled As Integer = 0
MyPartsMatrix (oD, oT, unfilled)
'Create a FBS item to add to the concurrent list collection
Dim FBSResultsItem = New FindBestResults
FBSResultsItem.oD = oD
FBSResultsItem.oT = oT
FBSResultsItem.unfilled = unfilled
'Add this item to the Concurent collection
ConcurrentListofResults.Enqueue(FBSResultsItem)
End Sub)
End Sub)
'Get best result.
Dim bestResult As FindBestResults
For Each item As FindBestResults In ConcurrentListofResults
If item.unfilled < bestResult.unfilled Then
bestResult.oD = item.oD
bestResult.oT = item.oT
bestResult.unfilled = item.unfilled
End If
Next
Public Sub MyPartsMatrix (ByVal oD As Integer, ByVal oT As Integer, ByRef unfilled As Integer)
'....do stuff with the unfilled variable....
'unfilled is a counter that is incremented while we run through the PartsMatrix
unfilled = unfilled + 1
End Sub
If this is not thread safe, is there another way to write this so that the "unfilled" variable is thread safe or to make calling another subroutine thread safe?
Without the definition of MakeSchedule (which you've called MyMakePartsMatrix in another place) it's impossible to say whether it's thread safe.
To be threadsafe the sub needs to not modify anything other than unfilled. This will ensure that calling it multiple times with the same inputs will always yield the same outputs. I'd also recommend converting to a function as I find this much easier to understand whats happening.
On another note:
Your performance will be better if you don't nest parallel loops. You're currently waiting for your inner loop to finish before launching your second loop. If you're using larger x + y values then something similar to the code will work better.
Dim scenarios = From x2 In Enumerable.Range(0, x)
From y2 In Enumerable.Range(0, y)
Select New With {x2, y2}
Parallel.ForEach(scenarios, Sub(s)
End Sub)
How do I return a result from a function?
For example:
Public Function test() As Integer
return 1
End Function
This gives a compile error.
How do I make this function return an integer?
For non-object return types, you have to assign the value to the name of your function, like this:
Public Function test() As Integer
test = 1
End Function
Example usage:
Dim i As Integer
i = test()
If the function returns an Object type, then you must use the Set keyword like this:
Public Function testRange() As Range
Set testRange = Range("A1")
End Function
Example usage:
Dim r As Range
Set r = testRange()
Note that assigning a return value to the function name does not terminate the execution of your function. If you want to exit the function, then you need to explicitly say Exit Function. For example:
Function test(ByVal justReturnOne As Boolean) As Integer
If justReturnOne Then
test = 1
Exit Function
End If
'more code...
test = 2
End Function
Documentation: Function Statement
VBA functions treat the function name itself as a sort of variable. So instead of using a "return" statement, you would just say:
test = 1
Notice, though, that this does not break out of the function. Any code after this statement will also be executed. Thus, you can have many assignment statements that assign different values to test, and whatever the value is when you reach the end of the function will be the value returned.
Just setting the return value to the function name is still not exactly the same as the Java (or other) return statement, because in java, return exits the function, like this:
public int test(int x) {
if (x == 1) {
return 1; // exits immediately
}
// still here? return 0 as default.
return 0;
}
In VB, the exact equivalent takes two lines if you are not setting the return value at the end of your function. So, in VB the exact corollary would look like this:
Public Function test(ByVal x As Integer) As Integer
If x = 1 Then
test = 1 ' does not exit immediately. You must manually terminate...
Exit Function ' to exit
End If
' Still here? return 0 as default.
test = 0
' no need for an Exit Function because we're about to exit anyway.
End Function
Since this is the case, it's also nice to know that you can use the return variable like any other variable in the method. Like this:
Public Function test(ByVal x As Integer) As Integer
test = x ' <-- set the return value
If test <> 1 Then ' Test the currently set return value
test = 0 ' Reset the return value to a *new* value
End If
End Function
Or, the extreme example of how the return variable works (but not necessarily a good example of how you should actually code)—the one that will keep you up at night:
Public Function test(ByVal x As Integer) As Integer
test = x ' <-- set the return value
If test > 0 Then
' RECURSIVE CALL...WITH THE RETURN VALUE AS AN ARGUMENT,
' AND THE RESULT RESETTING THE RETURN VALUE.
test = test(test - 1)
End If
End Function
Static variables in VBA are simple enough:
Public Sub foo()
Static i As Integer
i = i + 1
Debug.Print i
End Sub
outputs (when called multiple times):
1
2
3
...
The problem is, VBA does not support initializing a variable on the same line as the declaration (not counting using : to put two lines on one):
Public Sub foo()
Dim i As Integer = 5 'won't compile!
Dim j As Integer
j = 5 'we have to do this instead
End Sub
This clashes with static variables:
Public Sub foo()
Static i As Integer 'we can't put an initial value here...
i = 5 'so this is how we'd usually initialize it, but...
i = i + 1
Debug.Print i
End Sub
You can probably see what happens - The very first thing the variable does every time foo is called is set itself back to 5. Output:
6
6
6
...
How can you initialize a static variable in VBA to a value other than its default? Or is this just VBA dropping the ball?
One way to do this if you want to keep the static semantics and not switch to a global is to sniff the default value and then set the initial condition:
Static i As Integer
if (i = 0) then i = 5
Safer alternative would perhaps be
Static i As Variant
if isempty(i) then i = 5
Or
Public Sub foo(optional init as boolean = false)
Static i As Integer
if init then
i = 5
exit sub
endif
You could probably also create a class with a default property and use class_initialize but that's probably a bit over-fussy.
I had the same issue in VB6, where it's exactly the same, and I like the Microsoft recommendation most:
Sub initstatic ()
Static Dummy, V, I As Integer, S As String
' The code in the following if statement will be executed only once:
If IsEmpty(Dummy) Then
' Initialize the dummy variant, so that IsEmpty returns FALSE for
' subsequent calls.
Dummy = 0
' Initialize the other static variables.
V = "Great"
I = 7
S = "Visual Basic"
End If
Print "V="; V, "I="; I, "S="; S
' Static variables will retain their values over calls when changed.
V = 7
I = I + 7
S = Right$(S, 5)
End Sub
I solved it as follows using a static boolean to indicate if you are entering the function for the first time. This logic should work for other situation as well, i think
Private Sub Validate_Date(TB as MSForms.TextBox)
Static Previous_Value as Date
Static Not_First_Time as Boolean
if Not_First_Time = False Then
Previous_Value = Now
Not_First_Time = True
endif
if IsDate(TB.Value) = False then TB.Value = Previous_Value
Previous_Value = TB.Value
End sub
The use of a Boolean to flag something as already initialized functions correctly in normal use but has an unexpected side effect when using the debugger. bIsInitialized is NOT reset to False when the VBA projuect is re-compiled. When the initialization code (or the constants the code uses) is changed changed, the thing being initialized will not be re-initialized while using the debugger.
One work-around is to set a breakpoint at the statement "If (Not bIsInitialized) Then" and add bIsInitialized as a watch variable. When the breakpoint is reached the first time, Click on the value and change it to false, remove the breakpoint and use F5 to continue. There may be a better work-around that uses something that is reliably reset by recompiling the project, but since the documentation for VBA says that the Boolean would be re-initialed after going out of context and all code stopping, there's no way to know if that behavior was version dependent. Not reinitializing the block of memory associated with the routine appears to be a performance optimization.
Static bIsInitialized as Boolean
Static something_time_consuming_to_initialize
If (Not bIsInitialized) Then
initialize something_time_consuming_to_initialize
bIsInitialized = True
End If