[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

microsoft.public.dotnet.framework

Implement IEnumerable(Of T) and Inherit from CollectionBase

Paul Linville

7/9/2008 1:37:00 PM

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.
5 Answers

Paul Linville

7/9/2008 2:21:00 PM

0

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

Jeroen Mostert

7/9/2008 2:28:00 PM

0

Paul Linville wrote:
> 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.

Paul Linville

7/9/2008 2:51:00 PM

0

Ahhh. Perfect. Much better than my solution.

Thank you very much.

"Jeroen Mostert" wrote:

> Paul Linville wrote:
> > 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

7/9/2008 2:59:00 PM

0

Paul Linville wrote:
> 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.

<snip>
> 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...

--
J.

Pavel Minaev

7/10/2008 12:01:00 PM

0

On Jul 9, 5:37 pm, Paul Linville
<PaulLinvi...@discussions.microsoft.com> wrote:
> 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