Click to See Complete Forum and Search --> : Enhancing Looping With Enumerators


Scott.Macmaster
December 10th, 2008, 03:42 PM
Before using enumerators I would have have an exposed data structures and loops like this:


' Exposed data structures
Public Blocks As New SortedList(Of Integer, Block)
Public RedBlocks As New SortedList(Of Integer, Block)
Public BlueBlocks As New SortedList(Of Integer, Block)

' Loops
For Each blockKVP As KeyValuePair(Of Integer, Block) In Blocks
Dim block As Block = blockKVP.Value
...
Next

For Each blockKVP As KeyValuePair(Of Integer, Block) In BlueBlocks
Dim block As Block = blockKVP.Value
...
Next


Whenever code added/removed blocks to the list the code had to make sure to add/remove from each list. This of course is error prone. Also, if I wanted to maintain another list of blocks in a different color I'd have to modify each block of code that added/removed from the lists.

So I created enumerator classes to encapsulate all this and to protect the data structures. It also made the loop syntax simpler and easier to read.


' Enumerators
Public Class BlockEnumerator
Implements IEnumerator(Of Block)
...
End Class
Public Class RedBlockEnumator
Implements IEnumerator(Of Block)
....
End Class
Public Class BlockList
Implements IEnumerable(Of Block)
...
End Class
Public Class RedBlockList
Implements IEnumerable(Of Block)
...
End Class

' Protected data structures
Private _Blocks As New SortedList(of integer, Block)
Public Blocks As New BlockList(_Blocks)
Public RedBlocks As New BlockList(_Blocks)

' Simple loops
For Each block In Blocks
...
Next
For Each block In RedBlocks
...
Next


Now my question. I would like to only have one public list and to select the subset of blocks in the looping syntax. I have few thoughts what the syntax might be but I don't know what class/data structures I need in order to do this.

Code Examples

' Protected data structures
Private _Blocks As New SortedList(of integer, Block)
Public Blocks As New BlockList(_Blocks)

' Loops through all blocks
For Each block In Blocks
...
Next
For Each block In Blocks.AllBlocks
...
Next
For Each block In Blocks(BlockColor.AllColors)
...
Next
' Loops through red blocks
For Each block In Blocks.RedBlocks
...
Next
For Each block In Blocks(BlockColor.RedBlocks)
...
Next


Well, does anyone have any suggestions or know if this is even possible?


Thanks,

TheCPUWizard
December 10th, 2008, 03:46 PM
I would create a SINGLE class that hadd "All" the blocks, then expose enumerators that only returned the ones which met the requirements.

NO multiple collections to maintain, smaller, faster, less error prone, easier to use....whats not to like?

Marraco
December 11th, 2008, 05:59 AM
Before using enumerators I would have have an exposed data structures and loops like this:

' Loops
For Each blockKVP As KeyValuePair(Of Integer, Block) In Blocks
Dim block As Block = blockKVP.Value
...
Next

Whenever code added/removed blocks to the list the code had to make sure to add/remove from each list. This of course is error prone. Also, if I wanted to maintain another list of blocks in a different color I'd have to modify each block of code that added/removed from the lists.try:
' Loops
For Each blockKVP As KeyValuePair(Of Integer, Block) In Blocks.Clone
Dim block As Block = blockKVP.Value
...
NextThat way, yo would iterate on each object on the original SortedList, at the cost of duplicating your data structure before the For...Next. You can delete the same objects in the KeyValuePair, without ruining the logic under you For..Each...Next

TheCPUWizard
December 11th, 2008, 07:21 AM
Clonig the list usually has a bigger performance hit than just using a good pattern for handling addition/deletion. This can be implemented at the collection class level. It is also much easier to make thread safe by localizing all of the locking.

Scott.Macmaster
December 11th, 2008, 09:23 AM
Well, that's what I want to do, have everything in one class. I did some more thinking and found a solution. For some reason it didn't initial occur to me I could have a function in an enumerable class that returns an object of a different enumerable class.

This'll work perfectly for my block example. However, the project I'm actually working on involves the list ordered in different ways so I'll still need to manage multiple lists. However, it'll all be in one class and the enumerable objects returned by the functions in the primary enumerable object won't have add/remove functions so my data structures will be completely protected.


' Enumerators
Public Class BlocksByIdEnumerator
Implements IEnumerator(Of Block)
...
End Class
Public Class BlocksByNameEnumerator
Implements IEnumerator(Of Student)
...
End Class

' Enumerable classes
Public Class BlocksList
Implements IEnumerable(Of Block)

Private Blocks As New SortedList(of Integer, Block)
Private BlocksByName As New SortedList(of String, Block)

Public Functions ByName() As BlocksByNameList
Return New BlocksByNameList(BlocksByName)
End Function
...
End Class
Public Class BlocksByNameList
Implements IEnumerable(Of Block)

Private Blocks As SortedList(Of String, Block)
...
End Class

' Fully protected data structure
Public blocks As BlocksList

' Looping through blocks
For Each block In blocks
...
Next
' Loop through blocks in a different order
For Each block In blocks.ByName
...
Next


Well, I don't think this can be improved anymore.


Thanks,

TheCPUWizard
December 11th, 2008, 09:36 AM
Scott, since you posted abbriviated code, I can not be 100% certain, but it still looks as if there is a postential for error.

1) Create BlockList
2) Get Local Reference to BlockList.ByName
3) Remove item from Local Reference (of type BlocksByNameList)

Will the original BlockList properly reflect the change?

Also what happens if the caller tries "new BlocksByNameList" directly?

It seems that this implementation does NOT optimally meet the goal of "Make classes as DIFFICULT as possible to use INCORRECTLY" - Scott Meyers....

Scott.Macmaster
December 11th, 2008, 09:50 AM
I had said, "and the enumerable objects returned by the functions in the primary enumerable object won't have add/remove functions so my data structures will be completely protected"

Without a add/remove functions you can't modify the internal data structure with the local reference returned by BlockList.ByName.

I only have one constructor for BlocksByNameList


Public Sub New(blocksByName As SortedList(As String, Block))
Blocks = blocksByName
End Sub


So you can't initialize a BlocksByName object manually with the sortedlist inside of Blocks since there isn't a way to get a reference to it. Not that it would matter since it doesn't have add/remove functions. You could initialize it with your own sortedlist but since it doesn't have add/remove functions that would be pointless.

If you have thoughts on how my code could by misused I'd like to know.


Thanks,

dglienna
December 11th, 2008, 06:21 PM
Post it all. Attach it as a file if it is too big to paste.

TheCPUWizard
December 11th, 2008, 09:32 PM
I If you have thoughts on how my code could by misused I'd like to know.


I confess that I was looking much more ad the code, than the text. It seems you have most of the major bases covered, with the possible of some unnecessary object lifetime (this could only be confirmed with a fully compilable piece of code that could be carefully analyzed with a proper profiler.