6

Not a homework question, despite the bizarreness of the scenario. I've just substituted the real objects I'm working with to simplify the examples.

This has got me started here, but unsure how to proceed. I'm trying to write a class that contains a collection, and I'm getting lost in the world of IEnumerator and IEnumerable, which I'm very new to (and not even entirely sure I'm on the right path). Let's say I have a class:

Public Class BakedBean
    Private Shape As String
    Private Variety As String
    Private Flavour As String
    'etc
End Class

And another class to represent a collection of BakedBeans:

Public Class TinOfBeans
    '?
End Class

I want to be able to get Beans in the TinOfBeans class as a collection, so that I can make calls like these, but without being tied to a specific collection type:

Dim myTin As New TinOfBeans()
myTin.Add(New BakedBean(...))

For Each bean As BakedBean in myTin
    '...
Next

myTin(0).Flavour = "beany"

I've been looking at IEnumerable and IEnumerator, and I have this much so far, but I'm getting very lost with it:

BakedBean Class

Public Class BakedBean
    Private Shape As String
    Private Variety As String
    Private Flavour As String
    'etc
End Class

BeansEnumerator Class

Public Class BeansEnumerator
    Implements IEnumerator

    Private Position As Integer = -1

    Public ReadOnly Property Current As Object Implements System.Collections.IEnumerator.Current
        Get
            '???
        End Get
    End Property

    Public Function MoveNext() As Boolean Implements System.Collections.IEnumerator.MoveNext
        '???
    End Function

    Public Sub Reset() Implements System.Collections.IEnumerator.Reset
        Position = -1
    End Sub
End Class

TinOfBeans Class

Public Class TinOfBeans
    Implements IEnumerable

    Private beansEnum As BeansEnumerator

    Public Function GetEnumerator() As System.Collections.IEnumerator Implements System.Collections.IEnumerable.GetEnumerator
            Return beansEnum
    End Function
End Class

At this point I'm getting rather tied up in knots and have no idea how to proceed (or, as I said at the start, if this is even the right approach). Any suggestions?

Community
  • 1
  • 1
Kai
  • 2,050
  • 8
  • 28
  • 46
  • Let me see if I understand you. you want a class that is a List? – Alexandre Oct 29 '13 at 11:55
  • I want a class that is a collection, not necessarily a List. – Kai Oct 29 '13 at 11:57
  • May I ask you why do you need this? What is the problem to the standard collections? – Alexandre Oct 29 '13 at 11:59
  • Certainly, I could stick a `Private beans As List(Of BakedBeans)` in my `TinOfBeans` class, and expose that through a property, but then I'm tied down to a `List` forever. Should I later need to change that to a `Dictionary` or whatever (the world needs more bean dictionaries!), I risk breaking all the code that touches it. I can't just do away with the `TinOfBeans` class altogether and replace the whole thing with a `List(Of BakedBeans)`, because it will have additional methods (`Cook()` for example) that `List` doesn't have – Kai Oct 29 '13 at 12:21
  • I see, thanks for the information although i'm don't know how to do this I found this link that might be useful: http://msdn.microsoft.com/en-us/library/xth2y6ft(v=vs.71).aspx I hope this help you. – Alexandre Oct 29 '13 at 12:31

2 Answers2

8

You're making this way too hard. .Net already provides the exact classes you need, through the use of Generics.

I want to be able to ... make calls like these:

Dim myTin As New TinOfBeans()
myTin.Add(New BakedBean(...))

For Each bean As BakedBean in myTin
    '...
Next

myTin(0).Flavour = "beany"

Sure. Here's how:

Dim myTin As New List(Of BakedBean)()
myTin.Add(New BakedBean(...))

For Each bean As BakedBean in myTin 
   '...
Next

myTin(0).Flavour = "beany"

Every one of your lines maps exactly. Let Generic Collections do the work for you.

You seem to also be confused about IEnumerable, and I have one requirement left to implement:

make calls ... without being tied to a specific collection type

We can cover both of those with one technique. Let's define a method that accepts a Bean collection:

Public Sub CookBeans(ByVal beans As List(Of BakedBean))

However, this does not handle arrays, or Iterators, or other BakedBean collection types. The answer here is IEnumerable:

Public Sub CookBeans(BvVal beans As IEnumerable(Of BakedBean))
   For Each bean As BakedBean In beans
      Cook(bean)
   Next 
End Sub

This time I included an example implementation of the method, so that you can see how it will work. The important thing to understand here is that this method will allow you to call it and pass an Array, List, Iterator, or any other BakedBean collection.

This works because a List(Of BakedBean) is (or rather, implements) an IEnumerable(Of BakedBean). So does a BakedBean array and other .Net collections. IEnumerable is the root interface for all collections. You can have a concrete List(Of BakedBean) object in memory somewhere, but define your method parameters and return types using IEnumerable(Of BakedBean), and if you ever decide to change that List object to something else, those methods will all still work.

You can also return IEnumerable:

Public Function GetPintos(ByVal beans As IEnumerable(Of BakedBean)) As IEnumerable(Of BakedBean)
    Dim result As IEnumerable(Of BakedBean)
    result = beans.Where(Function(bean) bean.Variety = "pinto")
    Return result
End Function

And if you ever need that IEnumerable to become a list, it's pretty easy:

Dim beans As List(Of BakedBeans) = '... get beans from somewhere

Dim pintos As List(Of BakedBeans) = GetPintos(beans).ToList()
CookBeans(pintos)
Joel Coehoorn
  • 399,467
  • 113
  • 570
  • 794
  • "IEnumerable is the root interface for all collections." Genuinely didn't think about that. Looks like exactly what I'm after, since it doesn't constrain me to a specific collection. Perfect, thanks – Kai Oct 29 '13 at 14:43
  • 1
    @Kai Just be careful here: there is some hyperbole in that statement. It's the root interface for all collections provided in the framework, but I have seen custom collections in the wild that encapsulate arrays or lists without themselves implementing that interface. It's poor practice, but it happens. – Joel Coehoorn Oct 29 '13 at 14:46
  • I'll keep it in mind. This should serve my needs though, thanks. – Kai Oct 29 '13 at 14:58
  • @JoelCoehoorn one question, all methods you put like: Public Sub CookBeans(BvVal beans As IEnumerable(Of BakedBean)) , GetPintos should it still belong to BakedBean class or separated class as collection class storing private list object? – Arie Jan 29 '17 at 13:25
1

I think you might be complicated things a bit. Unless you really want to learn how to use IEnumerable, you just need to inherits a list.

Public Class TinOfBeans
    Inherits List(Of BakedBean)

End Class

Or have a list inside the Tin

Public Class TinOfBeans
    Public Property Beans As New List(Of BakedBean)

End Class
the_lotus
  • 12,668
  • 3
  • 36
  • 53