For Each loop encountered 'Collection was modified; enumeration operation may not execute', but I need to change list's size at runtime - vb.net

I'm attempting to code a Huffman Encoding system in VB, using .NET 6. Here is the main code as it currently stands, using 'ConstructionQueue' as a priority queue to handle the nodes being added to the 'Tree' list. 'Node' is an abstract class from which 'LeafNode' and 'InternalNode' inherit; at the time this class is run, ConstructionQueue is filled with only LeafNode's. My issue is that I need to add InternalNodes to the queue during this initialization process to be handled. Here is the current code:
Public Class HuffmanTree
Private Tree As New List(Of Node)
Public Sub New(ByVal ConstructionQueue As PriorityQueue)
Dim PairMade As Boolean = False
Dim NodesProcessed As Integer = 0
Dim TempWeight As Integer
For Each Node In ConstructionQueue.QueueFrame
If ConstructionQueue.GetFrontPointer <> 0 And ConstructionQueue.GetFrontPointer Mod 2 = 0 Then
PairMade = True
End If
Tree.Insert(NodesProcessed, Node)
If PairMade = True Then
Dim TempNode As New InternalNode(ConstructionQueue.QueueFrame(ConstructionQueue.GetFrontPointer).GetWeight + TempWeight)
TempNode.SetLeftPointer(Tree(NodesProcessed - 1))
TempNode.SetRightPointer(Tree(NodesProcessed))
ConstructionQueue.Enqueue(TempNode)
PairMade = False
Else
TempWeight = Node.GetWeight
End If
NodesProcessed += 1
ConstructionQueue.SetFrontPointer(NodesProcessed)
Next
End Sub
End Class
Collection was modified; enumeration operation may not execute.
is the error I receive on the 'Next' before End Sub. I completely understand why, the issue is that I can't alter the size of the 'QueueFrame' List, but I need a method of doing this that is going to allow me to go through each node in the list whilst being able to change its size, as there will be multiple instances where I need to add to the list as I'm using it.
Does anyone know of any sort of workaround or fix to this? I'd be happy to provide any more information needed.

Related

Retrieving information from a Class module VB.Net

Currently I'm trying to store a bunch of integers/Strings in a Class inserting the information isn't a problem but for some reason i can't figure out how to retrieve the information
Public Class HardwareCards
Public Property RackAmount() As Integer
End class
Inserting the information
Sub GrabAccessInfo()
Dim Hardware As New HardwareCards
Dim HardwareCollection As New Collection
Hardware.RackAmount = rst("RackAmount").Value
End Sub
Retrieving the information
Sub RackSlotAccess()
Dim type As Type = HardwareCards.GetType()
Dim typename As Integer = type.FullName
If HardwareCards.Hardware.DI32 >= 1 Then 'Inserting 32 bit Digital input card(s)
InsertDigAddresses(HardwareCards.Hardware.DI32, 32, "I", Slot, Rack)
End If
End sub
What do i need to do to get the infomation out of the Class Module?
You're referencing the type when calling HardwareCards, and not an initialized object. Notice how in GrabAccessInfo you declare and initialize an instance of HardwareCards into the variable Hardware. In order to access the information you assigned to the object variable Hardware, you would need to reference it in RackSlotAccess.
Sub RackSlotAccess(hardware As HardwareCards)
'Perform logic, evaluations on hardware. Example:
Dim currentRackAmount = hardware.RackAmount
End Sub

Can I create variables dynamically based on other variables?

I'm working on a VBA module to process lists of quote items. My current boggle is trying to stack full or partial sets of things from the quote lists, and I'm trying to figure out how to keep track of them.
The item lists do not have a consistent number of items; one might be a single item, another might be a hundred.
The system divides the cargo into four broad types (Pipes, Plates, Beams and Other) for the sake of selecting which calculator logic to use.
Is there any way to create variables on the fly to keep track of individual line items? For instance, deploying a spot of pseudocode:
Public Qty & "_" & Class & "-" & ClassCount As Integer
Is there any way to make something like that work, or is there a better way to do it?
I'm a bit sketchy on classes, and I really should start looking at them more as they're very powerful - this link will give you more info: http://www.cpearson.com/excel/classes.aspx
Expanding on Jasons comments this is one way of building the class, and I'm sure there's a much better way of doing it:
Add a Class Module to your project and name the module cls_Quote.
Add this code to the class module:
Option Explicit
Private sQuote As String
Private lQuantity As Long
Private lAnotherValue As Long
Public Property Let Quote(Value As String)
sQuote = Value
End Property
Public Property Get Quote() As String
Quote = sQuote
End Property
Public Property Let Quantity(Value As Long)
lQuantity = Value
End Property
Public Property Get Quantity() As Long
Quantity = lQuantity
End Property
Public Property Let AnotherValue(Value As Long)
lAnotherValue = Value
End Property
In a normal module add this code:
Option Explicit
Private MyQuotes As Collection
Public Sub Test()
Dim MyNewQuote As cls_Quote
Dim x As Long
Dim vIQuote As Variant
Dim FinalSum As Long
Set MyQuotes = New Collection
For x = 1 To 10
Set MyNewQuote = New cls_Quote
With MyNewQuote
.Quantity = x
.Quote = "Pipes"
.AnotherValue = x * 5
End With
MyQuotes.Add MyNewQuote
Next x
For Each vIQuote In MyQuotes
If vIQuote.Quote = "Pipes" Then
FinalSum = FinalSum + vIQuote.Quantity
End If
Next vIQuote
MsgBox "Total sum of Pipes is: " & FinalSum
End Sub
Note: In the For x loop I'm creating a new instance of the class each time.
Now just waiting for someone with more class programming experience to show a much better way of doing it. :)

Create or clear Hashtable instance for each new request of Report in SSRS

I have a report done via VS2008 SSRS that is uploaded to my Reporting Services Server, that report is called when the event GenerateReport is called (on button click)
Now, I have the following code:
Public Shared Dim gruposVolTotal As New System.Collections.HashTable()
Public Shared Dim gruposSeleccion As New System.Collections.HashTable()
Public Shared Dim gruposModVenta As New System.Collections.HashTable()
Dim grupoActual as String = ""
Public Shared Dim acumuladoSeleccion as Double = 0
Public Shared Dim acumuladoVolTotal as Double = 0
Public Shared Dim acumuladoModVenta as Double = 0
Public Shared Dim acumuladoAnterior as Double = 0
Function Acumulador(ByVal MiGrupo as String, ByVal value as Double, ByVal Campo as String) As Double
If (GrupoActual <> MiGrupo) Then
If (Campo = "ModVenta") then
If (not gruposModVenta.Contains(MiGrupo)) then
acumuladoModVenta =acumuladoModVenta + value
gruposModVenta.Add(MiGrupo,acumuladoModVenta )
Return acumuladoModVenta
Else
Return gruposModVenta(MiGrupo)
End If
End If
If (Campo = "RunningValue") then
acumuladoVolTotal=acumuladoVolTotal + value
If (not gruposVolTotal.Contains(MiGrupo)) then
acumuladoAnterior = acumuladoVolTotal + acumuladoAnterior
gruposVolTotal.Add(MiGrupo, acumuladoAnterior)
End If
Return gruposVolTotal(MiGrupo)
End If
If (Campo = "VolumenSeleccion") then
If(not gruposSeleccion.Contains(MiGrupo)) then
acumuladoSeleccion=acumuladoSeleccion + value
gruposSeleccion.Add(MiGrupo, acumuladoSeleccion)
Return acumuladoSeleccion
Else
return gruposSeleccion(MiGrupo)
End If
End If
End If
End Function
What is the problem with this:
If you execute this on VS2008 the results are correct if I change pages (remember on VS2008) the elements persists and that is good.
If you execute this via web application, the first time will execute correctly but the second time if you choose different parameters on the report the values will stay as the 1st time you execute it.
This is because the variables and elements of the hashtables are saved in session so until I dont close the webpage and enter it again the values of these Hashtables and variables will stay the same after the first time you execute it.
If you change the scope of the variables and Hashtables to public, private or directly dim (private) each request (change of pages aka break page) will reset the value of the variables and hashtables.
So the Idea is to utilize the code I posted but what Im asking if is there any way to reset or clear the elements of the hashtables each time I call the report and change the parameters.
I saw in a forum that another user created a new hashtable for each request but those Hashtables wont be garbage collected and that means a lot of memory usage.
Let me know if you need more information.
EDIT:
You change the pages via ReportViewer Control.
I tried using the following code:
Protected Overrides Sub OnInit()
gruposVolTotal.Clear()
gruposSeleccion.Clear()
gruposModVenta.Clear()
grupoActual = ""
acumuladoSeleccion = 0
acumuladoVolTotal = 0
acumuladoModVenta = 0
acumuladoAnterior = 0
End Sub
But it seems it deletes it for every change of page and not each time I open the report.

Threading Exception: The number of WaitHandles must be less than or equal to 64

The title is to make this easy to find for others having this error. I'm new to Threading, so this is really giving me heck. I'm getting this runtime error that crashed Cassini. This is code that I'm maintaining originally developed as a website project in VS 2003 and converted to a VS 2008 website project.
Important Info:
The number of objects in the manualEvents array is 128 in this case.
products is an array of Strings
Need to support .NET 2.0
For Each product As String In products
If Not product.Trim().ToUpper().EndsWith("OBSOLETE") Then
calls += 1
End If
Next
Dim results(calls - 1) As DownloadResults
'Dim manualEvents(calls - 1) As Threading.ManualResetEvent '128 objects in this case.
Dim manualEvents(0) As Threading.ManualResetEvent
manualEvents(0) = New Threading.ManualResetEvent(False)
'NOTE: I don't think this will work because what is not seen here, is that
' this code is being used to populate and cache a long list of products,
' each with their own category, etc. Am I misunderstanding something?
'initialize results structures
'spawn background workers
calls = 0
For Each product As String In products
If Not product.Trim().ToUpper().EndsWith("OBSOLETE") Then
Dim result As New DownloadResults
'manualEvents(calls) = New Threading.ManualResetEvent(False)
'Moved above For Each after declaration of variable
result.params.product = product
result.params.category = docType
'result.ManualEvent = manualEvents(calls)
result.ManualEvent = manualEvents(0)
result.Context = Me._context
results(calls) = result
Threading.ThreadPool.QueueUserWorkItem(AddressOf ProcessSingleCategoryProduct, results(calls))
Threading.Interlocked.Increment(calls) 'Replaces below incrementation
'calls += 1
End If
Next
Threading.WaitHandle.WaitAll(manualEvents) 'CRASHES HERE
Thread Helper Function (for the sake of completion)
Public Shared Sub ProcessSingleCategoryProduct(ByVal state As Object)
Dim drs As DownloadResults = CType(state, DownloadResults)
Dim adc As New cADCWebService(drs.Context)
drs.docs = adc.DownloadADC(drs.params.category, drs.params.product)
drs.ManualEvent.Set()
End Sub
You don't need an array of 128 manual events to check for completion of all 128 threads.
Create only one manual reset event and a plain integer starting at 128. Decrement that integer using Interlocked.Decrement at the end of ProcessSingleCategoryProduct, and only signal the event when the count reaches zero:
if (Interlocked.Decrement(ByRef myCounter) = 0) myEvent.Set();
Then declare only one Threading.ManualResetEvent as opposed to an array of them, and you can call WaitOne instead of WaitAll on it, and you are done.
See also usr's comment for an easier alternative in case you have .NET 4.

queing jobs in threadpool vb.net

i have 20,000 items in a queue, and i want to process them using the threadpool.
will this be the best way to do it?
for i as integer = 0 to 19999
ThreadPool.QueueUserWorkItem (PerformTask, ListTask(i))
next
Sub PerformTask(i as string)
' do the work here
end sub
How can i return or set ui control from the PerformTask sub?
You cannot.
However, you can allocate a container (array, list) with a different slot for each result, and write into it. Alternatively, you could pass an object into the worker method that holds both the input and the result. I’d use this method:
Class TaskObject
Dim Input As String
Dim Result As Whatever
End Class
Dim tasks As TaskObject() = New TaskObject(20000) { }
For i as Integer = 0 to tasks.Length - 1
ThreadPool.QueueUserWorkItem(PerformTask, tasks(i))
next
Sub PerformTask(arg As Object)
Dim task As TaskObject = DirectCast(arg, TaskObject)
' do the work here
end sub
Unrelated: you should always enable Option Strict in your projects. No exception. Your code has type errors that the compiler should detect.