Ways of implenting IEnumerable in VB
-
OK, I cannot get my head around the whether there is a difference between the 'classic' (perhaps long winded) way of doing it, multiple classes and the 'new' Yield route to (apparently) the same end. I can understand the classic route (I think), see the code below. Yield confuses the heck out of me, if I understand it correctly then it's absolutely genius, I get the idea of a state machine (I think) and logically it sort of makes sense but I am not at all confident. Can it possibly be this simple? You just make that one method as simple or complex as necessary for your situation? (Warning, silly example since arrays can already be enumerated, but it's easy to demo)
Public Class YieldExample
Implements IEnumerablePrivate \_Thing As Double() Public Iterator Function GetEnumerator() As IEnumerator Implements IEnumerable.GetEnumerator If IsNothing(Me.\_Thing) Then Throw New InvalidOperationException() End If For Index As Integer = 0 To Me.\_Thing.GetUpperBound(0) Yield Me.\_Thing(Index) Next End Function
End Class
What I understand to be the 'old way'.
Public Class ExampleEnumerable
Implements IEnumerable(Of Double)Private \_Thing As Double() Public Function GetEnumerator() As IEnumerator(Of Double) Implements IEnumerable(Of Double).GetEnumerator Return New ExampleEnumerator(Me.\_Thing) End Function Public Function GetEnumerator1() As IEnumerator Implements IEnumerable.GetEnumerator Return Me.GetEnumerator End Function
End Class
Public Class ExampleEnumerator
Implements IEnumerator(Of Double)Private \_Thing As Double() Private \_Index As Integer Private \_curItem As Double Public Sub New(ByVal Thing As Double()) Me.\_Thing = Thing Me.\_Index = -1 Me.\_curItem = Nothing End Sub Public ReadOnly Property Current As Double Implements IEnumerator(Of Double).Current Get If IsNothing(Me.\_curItem) Then Throw New InvalidOperationException() End If Return Me.\_curItem End Get End Property Public ReadOnly Property Current1 As Object Implements IEnumerator.Current Get Return Me.Current End Get End Property Public Function MoveNext() As Boolean Implements IEnumerator.MoveNext 'If we are already at the end then return false If Me.\_I
-
OK, I cannot get my head around the whether there is a difference between the 'classic' (perhaps long winded) way of doing it, multiple classes and the 'new' Yield route to (apparently) the same end. I can understand the classic route (I think), see the code below. Yield confuses the heck out of me, if I understand it correctly then it's absolutely genius, I get the idea of a state machine (I think) and logically it sort of makes sense but I am not at all confident. Can it possibly be this simple? You just make that one method as simple or complex as necessary for your situation? (Warning, silly example since arrays can already be enumerated, but it's easy to demo)
Public Class YieldExample
Implements IEnumerablePrivate \_Thing As Double() Public Iterator Function GetEnumerator() As IEnumerator Implements IEnumerable.GetEnumerator If IsNothing(Me.\_Thing) Then Throw New InvalidOperationException() End If For Index As Integer = 0 To Me.\_Thing.GetUpperBound(0) Yield Me.\_Thing(Index) Next End Function
End Class
What I understand to be the 'old way'.
Public Class ExampleEnumerable
Implements IEnumerable(Of Double)Private \_Thing As Double() Public Function GetEnumerator() As IEnumerator(Of Double) Implements IEnumerable(Of Double).GetEnumerator Return New ExampleEnumerator(Me.\_Thing) End Function Public Function GetEnumerator1() As IEnumerator Implements IEnumerable.GetEnumerator Return Me.GetEnumerator End Function
End Class
Public Class ExampleEnumerator
Implements IEnumerator(Of Double)Private \_Thing As Double() Private \_Index As Integer Private \_curItem As Double Public Sub New(ByVal Thing As Double()) Me.\_Thing = Thing Me.\_Index = -1 Me.\_curItem = Nothing End Sub Public ReadOnly Property Current As Double Implements IEnumerator(Of Double).Current Get If IsNothing(Me.\_curItem) Then Throw New InvalidOperationException() End If Return Me.\_curItem End Get End Property Public ReadOnly Property Current1 As Object Implements IEnumerator.Current Get Return Me.Current End Get End Property Public Function MoveNext() As Boolean Implements IEnumerator.MoveNext 'If we are already at the end then return false If Me.\_I
Dear Mike, I realise this is a late answer, so maybe you've already figured this out. But maybe it's still worth something, so here goes: I think the short answer is that Yield is absolutely genius. The 'classic' way as you put it actually is the same implementation over and over again, where only the return value varies. What yield does for you is that it allows you to construct an IEnumerable directly from a function (using Yield statements), such that every time MoveNext() is called, the function takes off where it left off until the next Yield statement. So, yes, you can just use yield whenever you want :) I think the only possible issue with it is that it's probably slower than a proper custom ('classic') implementation, so if you have A LOT of elements AND you notice a performance bottleneck in enumeration THEN you might consider getting your hands dirty. Best regards, Richard
-
Dear Mike, I realise this is a late answer, so maybe you've already figured this out. But maybe it's still worth something, so here goes: I think the short answer is that Yield is absolutely genius. The 'classic' way as you put it actually is the same implementation over and over again, where only the return value varies. What yield does for you is that it allows you to construct an IEnumerable directly from a function (using Yield statements), such that every time MoveNext() is called, the function takes off where it left off until the next Yield statement. So, yes, you can just use yield whenever you want :) I think the only possible issue with it is that it's probably slower than a proper custom ('classic') implementation, so if you have A LOT of elements AND you notice a performance bottleneck in enumeration THEN you might consider getting your hands dirty. Best regards, Richard