Home | Contact Us | FAQ | Search & Site Map | Link to Us
Sign In | Join | Other 45 Sites in Network
HomeAnnouncementsFree MagazinesWhite PapersSubmit Content
Discussion GroupsASP.NETWindows FormsLanguages.NET FrameworkVisual Studio.NET
Articles.NET FrameworkASP.NETToolsWindows Forms
.NET DirectoryOpen Source ProjectsUser GroupsWeb Resources
Related Topics
Visual Basic 6SQL ServerMS AccessOther DB ProductsMS Server ProductsMore Topics ...

.NET Forum / .NET Framework / New Users / January 2006

Tip: Looking for answers? Try searching our database.

Custom collection implementing IEnumerable<T>

Thread view: 
Enable EMail Alerts  Start New Thread
Thread rating: 
Jeff - 03 Jan 2006 21:57 GMT
Hi,

I have a custom collection that is basically a wrapper of an List<T>
collection.  I want to expose the IEnumerable<T> interface of the collection
to the world, so on my custom collection, I implement both
IEnumerable/IEnumerator, and IEnumerable<T>/IEnumerator<T>.  My code for
those interfaces just calls off to the internal collections's methods, such as

IEnumerator IEnumerable.GetEnumerator()
{
      return myinternalList.GetEnumerator();
}

I am trying to bind my custom list to a control that can bind to a
collection that implements IEnumerable and/or IList (I expose the IList
interface for the internal collection in a similar way).

The problem is, everytime I execute something like

myControl.DataSource = myCustomCollection

I get a NullReferenceException.  I can't really tell where it's coming from,
or what is null.  Neither my custom collection nor the internal IList
collection are null.

Any ideas?

Jeff
Jon Skeet [C# MVP] - 03 Jan 2006 22:12 GMT
> I have a custom collection that is basically a wrapper of an List<T>
> collection.  I want to expose the IEnumerable<T> interface of the collection
[quoted text clipped - 20 lines]
>
> Any ideas?

Is myControl null by any chance? Have you tried it in a debugger and
seen whether GetEnumerator is being called at all?

What does the stack trace say?

Signature

Jon Skeet - <skeet@pobox.com>
http://www.pobox.com/~skeet   Blog: http://www.msmvps.com/jon.skeet
If replying to the group, please do not mail me too

Jeff - 04 Jan 2006 13:22 GMT
myControl is not null, either.

GetEnumerator() is being called when the collection is bound to the control,
like you would expect.  After GetEnumerator() is called, I noticed that the
Current property is null, so I thought that might be the problem, but I
called MoveFirst() before returning the enumerator and that didn't have any
effect either.

The stack trace doesn't show much, other than the control calling a few
internal binding routines.  I am not able to debug the code where the
nullreferenceexception is actually being thrown.

> > I have a custom collection that is basically a wrapper of an List<T>
> > collection.  I want to expose the IEnumerable<T> interface of the collection
[quoted text clipped - 25 lines]
>
> What does the stack trace say?
Jon Skeet [C# MVP] - 04 Jan 2006 19:26 GMT
> myControl is not null, either.
>
[quoted text clipped - 7 lines]
> internal binding routines.  I am not able to debug the code where the
> nullreferenceexception is actually being thrown.

Could you post a short but complete program which demonstrates the
problem?

See http://www.pobox.com/~skeet/csharp/complete.html for details of
what I mean by that.

Signature

Jon Skeet - <skeet@pobox.com>
http://www.pobox.com/~skeet   Blog: http://www.msmvps.com/jon.skeet
If replying to the group, please do not mail me too

Jeff - 04 Jan 2006 20:15 GMT
Sure.  Unfortunately I cannot view your link due to firewall restrictions
where I work, but I have since fixed the exception, and now when I bind to
the control (DataGridView in this case), the grid is not populated.  Anyway,
here is the basic outline of the code:

This is my custom collection:

public class ObjectCollection<T> :  System.Collections.Generic.IList<T>,
System.Collections.Generic.IEnumerable<T>,
System.Collections.Generic.IEnumerator<T>
{

    List<T> _objectArray = new List<T>;

    IEnumerator<T> IEnumerable<T>.GetEnumerator()
   {
       return _objectArray.GetEnumerator();
   }

   IEnumerator IEnumerable.GetEnumerator()
   {
       return _objectArray.GetEnumerator();
   }

   T System.Collections.Generic.IEnumerator<T>.Current
   {
           get { return _objectArray.GetEnumerator().Current; }
   }

   object IEnumerator.Current
  {
     get { return _objectArray.GetEnumerator().Current; }
  }

   bool IEnumerator.MoveNext()
   {
       return _objectArray.GetEnumerator().MoveNext();
   }

   void IEnumerator.Reset()
   {
       throw new Exception("The method or operation is not implemented.");
   }

   public int IndexOf(T item)
   {
       return _objectArray.IndexOf(item);
   }

   public void Insert(int index, T item)
   {
       _objectArray.Insert(index, item);
   }

   public void RemoveAt(int index)
   {
       _objectArray.RemoveAt(index);
   }

   public T this[int index]
   {
       get
       {
           return _objectArray[index];
       }
       set
       {
           _objectArray[index] = value;
       }
   }

   public void Add(T newItem)
   {
       _objectArray.Add(newItem);
   }

   public void Clear()
   {
       _objectArray.Clear();
   }

   public bool Contains(T item)
   {
       return _objectArray.Contains(item);
   }

   public void CopyTo(T[] array, int arrayIndex)
   {
       _objectArray.CopyTo(array, arrayIndex);
   }

   public bool Remove(T item)
   {
       return _objectArray.Remove(item);
   }

   public int Count
   {
       get { return _objectArray.Count; }
   }

   public bool IsReadOnly
   {
       get { return false; }
   }

   public List<T> GetObjectArray()
   {
           return _objectArray;
   }

       public void Sort()
       {
           _objectArray.Sort();
       }
}

There is more too this class obviously.  It's being used as a base class for
strongly typed collections of different types, but these are all the relevant
methods I think.  As you can see, for the methods in the IList, IEnumerable,
and IEnumerator interfaces, the methods just call the same methods on the
_objectArray list.  

So, I try to execute code on a windows form like this

ObjectCollection<MyCustomObject> col = new ObjectCollection<MyCustomObject>;
dataGridView1.DataSource = col;

MyCustomObject is a custom object with about 10 properties, so each property
should be matched to a column in the DataGridView upon binding.  With the
code above, nothing is displayed in the grid, however, if I change the code
to this

ObjectCollection<MyCustomObject> col = new ObjectCollection<MyCustomObject>;
dataGridView1.DataSource = col.GetObjectArray();

everything works fine.  Note that GetObjectArray() is a function on the
ObjectCollection class that just returns the internal IList<T>.
   

> > myControl is not null, either.
> >
[quoted text clipped - 13 lines]
> See http://www.pobox.com/~skeet/csharp/complete.html for details of
> what I mean by that.
Jon Skeet [C# MVP] - 04 Jan 2006 20:24 GMT
> Sure.  Unfortunately I cannot view your link due to firewall restrictions
> where I work, but I have since fixed the exception, and now when I bind to
> the control (DataGridView in this case), the grid is not populated.  Anyway,
> here is the basic outline of the code:

<snip>

Well, here's a problem:

>     object IEnumerator.Current
>    {
[quoted text clipped - 5 lines]
>         return _objectArray.GetEnumerator().MoveNext();
>     }

What do you expect to happen if you call MoveNext() and then Current?
Instead of giving you the first value, it'll throw an exception -
because you've fetched a new enumerator in each call.

<snip>

Signature

Jon Skeet - <skeet@pobox.com>
http://www.pobox.com/~skeet   Blog: http://www.msmvps.com/jon.skeet
If replying to the group, please do not mail me too

Jeff - 04 Jan 2006 20:55 GMT
Jon, I think I see what you mean, but wouldn't _objectArray.GetEnumerator()
return the same object each time?

> > Sure.  Unfortunately I cannot view your link due to firewall restrictions
> > where I work, but I have since fixed the exception, and now when I bind to
[quoted text clipped - 20 lines]
>
> <snip>
Jon Skeet [C# MVP] - 04 Jan 2006 21:42 GMT
> Jon, I think I see what you mean, but wouldn't _objectArray.GetEnumerator()
> return the same object each time?

No. That would be a really bad idea. For instance, it would mean that:

o Two threads couldn't iterate through a collection at the same time
o You couldn't iterate in a "nested" fashion (eg to create a list of
 pairs of elements)
o Unless Reset were called each time you started iterating, you
 wouldn't have the faintest idea what was going on.

GetEnumerator() should always return a new, independent enumerator.

Signature

Jon Skeet - <skeet@pobox.com>
http://www.pobox.com/~skeet   Blog: http://www.msmvps.com/jon.skeet
If replying to the group, please do not mail me too

Jeff - 05 Jan 2006 13:40 GMT
Yeah, you're right.  It wouldn't make much sense for GetEnumerator to return
the same object each time called.

I took your advice and change my IEnumerable/IEnumerator implementation to
this:

IEnumerator<T> IEnumerable<T>.GetEnumerator()
   {
       //_enumerator is a private class member
       _enumerator = _objectArray.GetEnumerator();
       return _enumerator;
   }

   IEnumerator IEnumerable.GetEnumerator()
   {
        //_enumerator is a private class member
       _enumerator = _objectArray.GetEnumerator();
       return _enumerator;
   }

   T System.Collections.Generic.IEnumerator<T>.Current
   {
           get { return _enumerator.Current; }
   }

   object IEnumerator.Current
  {
     get { return _enumerator.Current; }
  }

   bool IEnumerator.MoveNext()
   {
       return _enumerator.MoveNext();
   }

   void IEnumerator.Reset()
   {
       throw new Exception("The method or operation is not implemented.");
   }

now, I can bind to a grid through the BindingSource object, i.e. my code is

BindingSource bs = new BindingSource();
bs.DataSource = myCollection;
myDataGridView.Datasource = bs;

From what I've ready though, it seems BindingSource just creates an internal
List object, and the grid is actually binding to that List object.  So, it's
not different than if I use my code's GetObjectArray() method to bind.  There
still appears to be something wrong with my IEnumerable interface
implementation.

> > Jon, I think I see what you mean, but wouldn't _objectArray.GetEnumerator()
> > return the same object each time?
[quoted text clipped - 8 lines]
>
> GetEnumerator() should always return a new, independent enumerator.
Jon Skeet [C# MVP] - 05 Jan 2006 18:18 GMT
> Yeah, you're right.  It wouldn't make much sense for GetEnumerator to return
> the same object each time called.
>
> I took your advice and change my IEnumerable/IEnumerator implementation to
> this:

<snip>

That's still not right though - it links calling MoveNext() on your
class to calling MoveNext() on whichever enumerator was last returned.
It may work for the moment, but it's not really right. Without
analysing it particularly closely, I think your
Current/MoveNext()/Reset() implementations are current, but
GetEnumerator() should just return _objectArray.GetEnumerator().

<snip>

> now, I can bind to a grid through the BindingSource object, i.e. my code is
>
[quoted text clipped - 7 lines]
> still appears to be something wrong with my IEnumerable interface
> implementation.

Maybe the above will help. Do you need to implement *all* those
interfaces? Could you get away with doing *either* IEnumerator *or*
IEnumerable? (And the generic version of whichever one?)

Signature

Jon Skeet - <skeet@pobox.com>
http://www.pobox.com/~skeet   Blog: http://www.msmvps.com/jon.skeet
If replying to the group, please do not mail me too


Free Magazines

Get these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...

Oracle MagazineNetwork ComputingComputer WorldBio-IT WorldeWeekInformation WeekInfosecurity
 
Sign In
Join
My Latest Posts
My Monitored Threads
My Blog
My Photo Gallery
My Profile
My Homepage

Start New Thread
Enable EMail Alerts
Rate this Thread



©2008 Advenet LLC   Privacy Policy - Terms of Use
This website includes both content owned or controlled by Advenet as well as content owned or controlled by third parties.