Implementing For Each. I'm guessing this is to do with internal pointers.

Hi,

There is a sample in the help for VS called "HOW TO: Make a Visual Basic .NET Class Usable in a For Each Statement".

I wanted to use it for enumerating tax classes in a financial program as it would make the code more readable and lines shorter...So I modified the example only change was to add runtime initialisation of the items rather than a fixed set as in the example and it semi-works...

In the example it passes only one iteration of the for each loop my application uses it many times and each instance after the first iteration fails with the loop never executing as it is at the end of the set. Applied this to the example as is and it has the same behaviour, basically the reset is never called to put the set back to start position.

Original Example:

Sub Main()
Dim c As car
Dim t As String = vbTab & vbTab
Dim n As String = vbCrLf
Dim carz As cars = New cars()

Console.WriteLine(n & "Internal Collection" & n)
For Each c In carz
Console.WriteLine(c.Make & t & c.Year)
Next

Console.ReadLine()
End Sub

MODIFIED:

Sub Main()
Dim c As car
Dim t As String = vbTab & vbTab
Dim n As String = vbCrLf
Dim carz As cars = New cars()

Console.WriteLine(n & "Internal Collection" & n)

For Each c In carz
Console.WriteLine(c.Make & t & c.Year)
Next

Console.ReadLine()

Console.WriteLine(n & "Internal Collection" & n)
For Each c In carz
Console.WriteLine(c.Make & t & c.Year)
Next

Console.ReadLine()

End Sub

The second iteration never happens. Any help appreciated.

Michael.




Answer this question

Implementing For Each. I'm guessing this is to do with internal pointers.

  • MSDevUser

    Hi,

    Why not use Ubound instead of FOR EACH to get the upperBound limit of your array >>

    Dim index , loopIndexas integer

    index=Ubound(carz)

    For loopIndex=0 to index

    Console.WriteLine(carz(loopIndex).c.Make & _

    t & (carloopIndex).c.Year) ' or similiar as you can have an array

    'of any kind of object or CLASS

    Next

    Regards,

    S_DS



  • mm_ezzo

    No internal pointer the class implements enumeration and there is a reset function that I assume should be called as part of the FOR initialisation, that routine sets the position variable to -1 which is just before the beginning of the set as the first iteration adds one to it. That I do not have a problem with, just wondering why if a reset function is required it is never called or why the FOR EACH is not calling it as part of its initialise. See the example code in Help.

    Michael.



  • motorola

    Can you show the code for "cars"

    In order to participate in for each, you must implement the enumerator pattern correctly. See this article for more details. In particular make sure that you have implemented Reset() correctly. You can set a breakpoint in your implementation of Reset() and MoveNext() to be sure that everything works as expected.


  • sudheer_316

    Here is the complete modified code with my own implementation (Using Tax not Cars) of dynamically adding TaxEntry at runtime and solution to make the position initialise to -1 on the first run through after it has run through... THis is just the test routine I used to try to understand it, much more functionality and behaviour has been added to the final working solution, I would encourage anyone to use FOR EACH as it simplifies code once you get your head round it, doing a walkthrough helps.


    Option Explicit On
    Option Strict On
    Imports System.Collections

    Public Class Taxes : Implements IEnumerator, IEnumerable
    Private position As Integer = -1
    Private TaxTables(0) As TaxEntry

    Public Sub Add(ByVal Code As Integer, ByVal Percent As Decimal)

    If Not TaxTables(TaxTables.GetUpperBound(0)) Is Nothing Then
    ReDim Preserve TaxTables(TaxTables.GetUpperBound(0) + 1)
    End If

    TaxTables(TaxTables.GetUpperBound(0)) = New TaxEntry(Code, Percent)

    End Sub

    Public Function GetEnumerator() As IEnumerator Implements IEnumerable.GetEnumerator
    Return CType(Me, IEnumerator)
    End Function

    Public Function MoveNext() As Boolean Implements IEnumerator.MoveNext
    '
    ' If we have walked through once then position will not be < length so this must be a new
    ' run through so set position to 0.
    '
    If (position < TaxTables.Length) = False Then
    position = 0
    else
    position += 1
    End If

    Return (position < TaxTables.Length)
    End Function

    Public Sub Reset() Implements IEnumerator.Reset
    position = -1
    End Sub

    Public ReadOnly Property Current() As Object Implements IEnumerator.Current
    Get
    Return CType(TaxTables(position), TaxEntry)
    End Get
    End Property
    End Class


    Imports System.Collections
    Public Class TaxEntry
    Private myTaxCode As Integer
    Private myPercentage As Decimal
    Private myTaxAmount As Decimal

    Public Sub New(ByVal Code As Integer, ByVal Percent As Decimal)
    myTaxCode = Code
    myPercentage = Percent
    myTaxAmount = 0.0
    End Sub

    Public Property TaxCode() As Integer
    Get
    Return myTaxCode
    End Get
    Set(ByVal value As Integer)
    myTaxCode = value
    End Set
    End Property

    Public Property TaxPercent() As Decimal
    Get
    Return myPercentage
    End Get
    Set(ByVal Value As Decimal)
    myPercentage = Value
    End Set
    End Property

    Public Property TaxAmount() As Decimal
    Get
    Return myTaxAmount
    End Get
    Set(ByVal Value As Decimal)
    myTaxAmount = Value
    End Set
    End Property
    End Class


    Module Module1

    Sub Main()
    Dim TaxTest As New Taxes()

    TaxTest.Add(1, 0.0)
    TaxTest.Add(2, 17.5)

    Dim v As TaxEntry

    For Each v In TaxTest
    Console.WriteLine(v.TaxCode & " " & v.TaxPercent)
    Next

    Console.ReadLine()

    For Each v In TaxTest
    Console.WriteLine(v.TaxCode & " " & v.TaxPercent)
    Next

    Console.ReadLine()
    End Sub
    End Module



  • GarethKeen

     

    Hi,

    Try the CODE IN THIS COLOUR FIRST.

    Try doing the next run through within a new sub or set a GLOBAL variable for the number of times you want the ForEach loop executed.

    i.e.>>

    The 1st line here would be after the>>

    WINDOWS FORM DESIGNER GENERATED CODE section.

    Private iterationsCount As Integer=2

    Sub Main()
    Dim c As car
    Dim t As String = vbTab & vbTab
    Dim n As String = vbCrLf
    Dim carz As cars = New cars()

    Console.WriteLine(n & "Internal Collection" & n)

    For Each c In carz
     Console.WriteLine(c.Make & t & c.Year)
    Next

    Console.ReadLine()

    If iterationsCount >0 Then

      iterationsCount =iterationsCount -1

      Main()

    Else

      Exit Sub

    End If

    End Sub

    ____________________________________________________

    Alternatively try the code with the 1st readline taken out.

    My guess it ReadLine is resetting the pointer in the collection of objects or moving the pointer past the collection end point.

    Sub Main()
    Dim c As car
    Dim t As String = vbTab & vbTab
    Dim n As String = vbCrLf
    Dim carz As cars = New cars()

    Console.WriteLine(n & "Internal Collection" & n)

    For Each c In carz
     Console.WriteLine(c.Make & t & c.Year)
    Next

              '    Console.ReadLine() 'TRY TAKING THIS LINE OUT

    Console.WriteLine(n & "Internal Collection" & n)
    For Each c In carz
     Console.WriteLine(c.Make & t & c.Year)
    Next

    Console.ReadLine()

    End Sub

    My final idea is to have the main Sub call the rest of the code

    the number of times you want it to, within a FOR NEXT loop

    as in>>

    Sub Main ()

    Dim index as Integer=0

    For index=1 to 2

    LoopCode()

    Next

    End Sub

    Private Sub LoopCode()
    Dim c As car
    Dim t As String = vbTab & vbTab
    Dim n As String = vbCrLf
    Dim carz As cars = New cars()

    Console.WriteLine(n & "Internal Collection" & n)
    For Each c In carz
     Console.WriteLine(c.Make & t & c.Year)
    Next

    Console.ReadLine()

    End Sub

    By the way i've not actally tried any of the above, they are just suggestions, i hope one works for you. :-)

     

    Regards,

     

    S_DS

     



  • Implementing For Each. I'm guessing this is to do with internal pointers.