Implement IEnumerable(Of T) and Inherit from CollectionBase

P

Paul Linville

A base Class in a framework looks thusly

Public MustInherit Class BindableCollectionBase(Of T As IBusinessBase)
Inherits CollectionBase
Implements IBindingList


I want to implement the IEnumerable(Of T) as well. Everything I have tried
leads to either stackoverflow or invalid cast exceptions.

How do I implement the generic IEnumerable(Of T) and also inherit from the
CollectinoBase? I cannot, at this time, switch to inheriting the
Collection(Of T) class because this is a base class in a framework and doing
so would cause much craziness.
 
P

Paul Linville

I think I got it working but not real happy with the implementation. It seems
like there would be some performance issues.

Any better ideas? Or, more importantly, will this actually work?


Public MustInherit Class BindableCollectionBase(Of T As IBusinessBase)
Inherits CollectionBase
Implements IBindingList


Implements System.Collections.Generic.IEnumerable(Of T)
Public Function GetEnumerator1() As
System.Collections.Generic.IEnumerator(Of T) Implements
System.Collections.Generic.IEnumerable(Of T).GetEnumerator
Dim lst As New List(Of T)
For Each item As T In Me
lst.Add(item)
Next

Return New BindableBaseEnumerator(Of T)(lst)
'CType(MyBase.GetEnumerator, Global.System.Collections.Generic.IEnumerator(Of
T))
End Function
.....
End Class

Public Class BindableBaseEnumerator(Of T As IBusinessBase)
Implements IEnumerator(Of T)

Public ReadOnly Property Current() As T Implements
System.Collections.Generic.IEnumerator(Of T).Current
Get
Try
Return lvLst(position)
Catch ex As Exception
Throw (New InvalidOperationException())
End Try

End Get
End Property

Public ReadOnly Property Current1() As Object Implements
System.Collections.IEnumerator.Current
Get
Try
Return lvLst(position)
Catch ex As Exception
Throw (New InvalidOperationException())
End Try
End Get
End Property

Dim position As Integer = -1

Private lvLst As List(Of T)
Public Sub New(ByVal list As List(Of T))
lvLst = list
End Sub

Public Function MoveNext() As Boolean Implements IEnumerator.MoveNext
position = position + 1
Return (position < lvLst.Count)
End Function

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

Private disposedValue As Boolean = False ' To detect
redundant calls

' IDisposable
Protected Overridable Sub Dispose(ByVal disposing As Boolean)
If Not Me.disposedValue Then
If disposing Then
' TODO: free other state (managed objects).
End If

' TODO: free your own state (unmanaged objects).
' TODO: set large fields to null.
End If
Me.disposedValue = True
End Sub

#Region " IDisposable Support "
' This code added by Visual Basic to correctly implement the
disposable pattern.
Public Sub Dispose() Implements IDisposable.Dispose
' Do not change this code. Put cleanup code in Dispose(ByVal
disposing As Boolean) above.
Dispose(True)
GC.SuppressFinalize(Me)
End Sub
#End Region

End Class
 
J

Jeroen Mostert

Paul said:
A base Class in a framework looks thusly

Public MustInherit Class BindableCollectionBase(Of T As IBusinessBase)
Inherits CollectionBase
Implements IBindingList


I want to implement the IEnumerable(Of T) as well. Everything I have tried
leads to either stackoverflow or invalid cast exceptions.

How do I implement the generic IEnumerable(Of T) and also inherit from the
CollectinoBase? I cannot, at this time, switch to inheriting the
Collection(Of T) class because this is a base class in a framework and doing
so would cause much craziness.

Well, this should work:

Public Overloads Function GetEnumerator() As IEnumerator(Of T) Implements
IEnumerable(Of T).GetEnumerator
...
End Function

The problem is filling in the "...", of course. If BindableCollectionBase
does not implement IEnumerator(Of T), you cannot defer to a base class
implementation to get the desired enumerator, since there isn't any -- the
non-generic enumerators aren't good enough. You can use a wrapper class for
this purpose:

Public Structure GenericEnumeratorAdapter(Of T)
Implements IEnumerator(Of T)

Private Inner As IEnumerator
Public Sub New(ByVal Inner As IEnumerator)
Me.Inner = Inner
End Sub

Public ReadOnly Property Current() As T Implements IEnumerator(Of
T).Current
Get
Return CType(Inner.Current, T)
End Get
End Property

Private ReadOnly Property UntypedCurrent() As Object Implements
IEnumerator.Current
Get
Return Inner.Current
End Get
End Property

Public Function MoveNext() As Boolean Implements IEnumerator.MoveNext
Return Inner.MoveNext
End Function

Public Sub Reset() Implements IEnumerator.Reset
Inner.Reset()
End Sub

Public Sub Dispose() Implements IDisposable.Dispose
CType(Inner, IDisposable).Dispose()
End Sub
End Structure

Now it's a simple matter of writing:

Public Overloads Function GetEnumerator() As IEnumerator(Of T) Implements
IEnumerable(Of T).GetEnumerator
Return New GenericEnumeratorAdapter(Of T)(MyBase.GetEnumerator)
End Function
 
J

Jeroen Mostert

Paul said:
I think I got it working but not real happy with the implementation. It seems
like there would be some performance issues.

Any better ideas? Or, more importantly, will this actually work?


Public MustInherit Class BindableCollectionBase(Of T As IBusinessBase)
Inherits CollectionBase
Implements IBindingList


Implements System.Collections.Generic.IEnumerable(Of T)
Public Function GetEnumerator1() As
System.Collections.Generic.IEnumerator(Of T) Implements
System.Collections.Generic.IEnumerable(Of T).GetEnumerator
Dim lst As New List(Of T)
For Each item As T In Me
lst.Add(item)
Next
Well, yes, if you allocate a new list for every iteration there *will* be
performance issues... There's no need for that. See my previous post.

Public ReadOnly Property Current() As T Implements
System.Collections.Generic.IEnumerator(Of T).Current
Get
Try
Return lvLst(position)
Catch ex As Exception
Throw (New InvalidOperationException())
End Try
This is poor style (no pun intended).

First, do not catch the general type Exception -- you can't do anything
meaningful with it except perhaps at the outermost level of your
application, where you could log it. If for example the framework threw an
OutOfMemoryException or a ThreadAbortException here, you would not want to
mask this by throwing an InvalidOperationException.

Second, there is no point here to catching exceptions at all, because you
know exactly when the error should occur: when .MoveNext() hasn't been
called after creation or after .Reset(), or the last call to .MoveNext()
returned false. In this case, this all translates to "position" being valid.
Relying on List to throw an ArgumentOutOfRangeException for you can mask
errors and false assumptions, and they obfuscate the conditions under which
your code can and should fail.

Here, it isn't likely that List contains a bug, or that it will one day not
throw ArgumentOutOfRangeException, or that it will one day throw something
else, but in general it's unwise to depend on exact error behavior as you're
doing here. It's a simple matter of implementing the checks yourself so you
don't introduce the dependency in the first place.

This seems like a long sermon for a short bit of throwaway code, but I see
this sort of thing all the time and I'm usually the unlucky schmuck who has
to fix the resulting code, so I figure that spreading the word as much as
possible may make someone's life a tiny bit happier...
 
P

Pavel Minaev

A base Class in a framework looks thusly

    Public MustInherit Class BindableCollectionBase(Of T As IBusinessBase)
        Inherits CollectionBase
        Implements IBindingList

I want to implement the IEnumerable(Of T) as well. Everything I have tried
leads to either stackoverflow or invalid cast exceptions.

How do I implement the generic IEnumerable(Of T) and also inherit from the
CollectinoBase?  I cannot, at this time, switch to inheriting the
Collection(Of T) class because this is a base class in a framework and doing
so would cause much craziness.

If you're using .NET 3.5, the simplest would be to do this:

Private Function GetEnumerator1() As
System.Collections.Generic.IEnumerator(Of T) Implements
System.Collections.Generic.IEnumerable(Of T).GetEnumerator
Return Me.Cast(Of T)().GetEnumerator()
End Function

You can probably also use that code with LINQbridge on .NET 2.0/3.0
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top